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

How does Angular destroy event handlers and property bindings when a component is destroyed

我试图了解 Angular 组件的销毁过程,比我在文档中找到的要详细一些。我希望这里有人能够回答以下问题:

是否在移除组件模板中的元素的事件侦听器之前移除这些元素的属性?

在Component的销毁过程中,事件监听器的注销是什么时候发生的,如何发生的?

在 Angular 中是否有关于在内部删除事件侦听器的过程的更多信息?

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

div.children
   span

到 "destroy" 一个跨度,您只需将它从 div.children 中删除。如果没有更多链接指向 span 元素,它将被垃圾回收。对象也是如此。

设想Angular中的以下结构:

ComponentA.nodes
   ComponentBElement -> ComponentBClass

现在 Angular 需要 "destroy" ComponentB。为此,它只需将 ComponentBElement 从父 ComponentA.nodes 中分离出来。这就是 Angular 所做的,例如,当您执行 viewContainerRef.clear():

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

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

是否需要显式调用removeEventListnersUsually 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 方法)。

当 Angular 创建组件视图时它调用 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 销毁视图时,将执行这些一次性用品并删除事件侦听器。:

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

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