且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

销毁组件时,Angular如何销毁事件处理程序和属性绑定

更新时间:2022-06-24 14:51:54

在JavaScript中,您本身无法删除DOM节点.如果您具有以下DOM树:

In JavaScript you cannot remove a DOM node per-se. If you have the following DOM tree:

div.children
   span

要破坏"跨度,只需要将其从div.children中删除.如果没有指向span元素的链接,则将对其进行垃圾回收.对于对象也是如此.

To "destroy" a span you simply need to remove it from the div.children. If there are no more links pointing to the span element it will be garbage collected. And the same holds true for objects.

想象一下Angular中的以下结构:

Imagine the following structure in Angular:

ComponentA.nodes
   ComponentBElement -> ComponentBClass

现在,Angular需要销毁" ComponentB.为此,只需将ComponentBElement与父ComponentA.nodes分离.这就是Angular的功能,例如,当您执行viewContainerRef.clear():

Now Angular needs to "destroy" ComponentB. To do that it simply detaches ComponentBElement from the parent ComponentA.nodes. And this is what Angular does, for example, when you execute viewContainerRef.clear():

function execRenderNodeAction(...) {
  const renderer = view.renderer;
  switch (action) {
    ...
    case RenderNodeAction.RemoveChild:
      renderer.removeChild(parentNode, renderNode);
      break; 

现在,假设Angular向ComponentBElement或其子级添加了一些事件侦听器.

Now, suppose Angular added some event listeners to ComponentBElement or its children.

是否需要显式调用removeEventListners? 通常不会,因为一旦删除了DOM元素,事件监听器也会被垃圾回收.但是,有可能在某些异步任务或继续存在的对象中捕获了对事件侦听器的引用.这样可以防止侦听器和DOM被垃圾回收.因此Angular确保删除事件监听器(在v5中为

Is there any need to explicitly call removeEventListners? Usually no, because once DOM elements are removed the event listeners are garbage collected as well. However, there's a possibility that a reference to the event listener is captured in some async task or an object that continues to live. This prevents the listener and the DOM from being garbage collected. So Angular ensures that event listeners are removed (in v5 it's DomEventsPlugin.removeEventListener method).

当Angular创建组件视图时,它调用

When Angular creates a component view it calls listenToElementOutputs:

function listenToElementOutputs(view, compView, def, el) {
    for (var i = 0; i < def.outputs.length; i++) {
        ...
        var disposable = listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure));
        ((view.disposables))[def.outputIndex + i] = disposable; <------
    }
}

您可以看到使用renderer附加了事件,然后将取消订阅回调(一次性)存储到view.disposables中.当Angular销毁视图时,将执行这些一次性对象,并删除事件侦听器.

You can see that event is attached using renderer and then the unsubscription callback (disposable) is stored into view.disposables. When Angular destroys a view these disposables are executed and event listeners are removed.:

function [destroyView](view) {
    ...
    if (view.disposables) {
        for (var i = 0; i < view.disposables.length; i++) {
            view.disposables[i](); <----------------
        }
    }

要了解有关视图和编译的更多信息,请阅读:

To learn more about views and compilation read:

  • Here is what you need to know about dynamic components in Angular
  • Here is why you will not find components inside Angular