AngularJS - $ destroy是否删除事件监听器?

dma*_*man 197 memory-leaks angularjs

https://docs.angularjs.org/guide/directive

通过侦听此事件,您可以删除可能导致内存泄漏的事件侦听器.注册到范围和元素的监听器在销毁时会自动清理,但如果您在服务上注册了监听器,或者在未删除的DOM节点上注册了监听器,则必须自行清理或者你冒着引入内存泄漏的风险.

最佳实践:指令应该自行清理.您可以使用element.on('$ destroy',...)或范围.$ on('$ destroy',...)来删除指令时运行清理函数.

题:

element.on "click", (event) ->我的指令里面有一个:

  1. 当指令被销毁时,是否有任何内存引用element.on以防止它被垃圾收集?
  2. Angular文档声明我应该使用处理程序来删除$destroy发出的事件上的事件侦听器.我的印象是destroy()删除了事件监听器,是不是这样?

tas*_*ATT 431

事件听众

首先,了解有两种"事件监听器"非常重要:

  1. Scope事件监听器通过$on以下方式注册

    $scope.$on('anEvent', function (event, data) {
      ...
    });
    
    Run Code Online (Sandbox Code Playgroud)
  2. 通过例如on或附加到元素的事件处理程序bind:

    element.on('click', function (event) {
      ...
    });
    
    Run Code Online (Sandbox Code Playgroud)

$范围.$摧毁()

$scope.$destroy()执行它会删除通过注册的所有监听器$on上$范围.

不会删除第二种DOM元素或任何附加的事件处理程序.

这意味着$scope.$destroy()从指令的链接函数中的示例手动调用将不会删除通过例如附加的处理程序element.on,也不会删除DOM元素本身.


element.remove()

请注意,这remove是一个jqLit​​e方法(如果在AngularjS之前加载jQuery,则为jQuery方法),并且在标准DOM元素对象上不可用.

element.remove()执行该元素及其所有子元素将从DOM中移除时,将通过例如附加所有事件处理程序element.on.

不会破坏与元素关联的$ scope.

为了使它更加混乱,还有一个名为的jQuery事件$destroy.有时在使用删除元素的第三方jQuery库时,或者如果手动删除它们,可能需要在发生这种情况时执行清理:

element.on('$destroy', function () {
  scope.$destroy();
});
Run Code Online (Sandbox Code Playgroud)

指令被"销毁"时该怎么办

这取决于指令如何被"销毁".

正常情况是指令被销毁,因为ng-view更改了当前视图.当发生这种情况时,该ng-view指令将销毁关联的$ scope,切断对其父作用域的所有引用并调用remove()该元素.

这意味着如果该视图在其链接函数中包含一个指令,当它被销毁时ng-view:

scope.$on('anEvent', function () {
 ...
});

element.on('click', function () {
 ...
});
Run Code Online (Sandbox Code Playgroud)

两个事件侦听器都将自动删除.

但是,重要的是要注意这些侦听器中的代码仍然可能导致内存泄漏,例如,如果您已经实现了常见的JS内存泄漏模式circular references.

即使在正常情况下,由于视图更改而导致指令被破坏,您可能需要手动清理.

例如,如果您已注册了一个监听器$rootScope:

var unregisterFn = $rootScope.$on('anEvent', function () {});

scope.$on('$destroy', unregisterFn);
Run Code Online (Sandbox Code Playgroud)

这是必需的,因为$rootScope在应用程序的生命周期中永远不会被破坏.

如果您使用的另一个pub/sub实现在$ scope被销毁时不会自动执行必要的清理,或者您的指令将回调传递给服务,则同样如此.

另一种情况是取消$interval/ $timeout:

var promise = $interval(function () {}, 1000);

scope.$on('$destroy', function () {
  $interval.cancel(promise);
});
Run Code Online (Sandbox Code Playgroud)

如果您的指令将事件处理程序附加到元素(例如,在当前视图之外),则还需要手动清除它们:

var windowClick = function () {
   ...
};

angular.element(window).on('click', windowClick);

scope.$on('$destroy', function () {
  angular.element(window).off('click', windowClick);
});
Run Code Online (Sandbox Code Playgroud)

这些是当Angular"销毁"指令时要做什么的一些例子,例如ng-viewor或ng-if.

如果您有自定义指令来管理DOM元素的生命周期等,它当然会变得更复杂.

  • '$ rootScope在应用程序的生命周期内永远不会被销毁.:一旦你想到它,就会很明显.这就是我所缺少的. (4认同)
  • @YashikaGarg只有一个调用所有监听器的辅助函数可能是最容易的.就像$ scope.$ on('$ destroy'),function(){ListenerName1(); ListenerName2(); ...}); 对于非隔离范围的事件处理程序,$是否有额外的复杂性?或者使用双向绑定隔离范围? (2认同)