1. ホーム
  2. angularjs

[解決済み】AngularJS - $destroyは、イベントリスナーを削除しますか?

2022-04-13 05:35:24

質問

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

このイベントをリッスンすることで、メモリリークの原因となりうるイベントリスナーを削除することができます。スコープや要素に登録したリスナーは破棄される際に自動的にクリーンアップされますが、サービスにリスナーを登録した場合や、削除されないDOMノードにリスナーを登録した場合は、自分でクリーンアップしなければメモリリークを引き起こす危険性があります。

ベストプラクティス。ディレクティブは自分自身で後始末をする必要があります。element.on('$destroy', ...) や scope.$on('$destroy', ...) を使って、ディレクティブが削除されたときにクリーンアップ関数を実行させることができます。

質問です。

私は element.on "click", (event) -> をディレクティブの中に入れてください。

  1. ディレクティブが破棄されたとき、そのディレクティブの中に element.on ガベージコレクションされないようにするため?
  2. Angularのドキュメントによると、イベントリスナーを削除するためにハンドラを使用する必要があります。 $destroy が発行されました。という印象を持ちました。 destroy() はイベントリスナーを削除していますが、そうではないのでしょうか?

解決方法は?

イベントリスナー

まず最初に、イベントリスナーには2つの種類があることを理解しておくことが重要です。

  1. で登録したスコープイベントリスナー。 $on :

    $scope.$on('anEvent', function (event, data) {
      ...
    });
    
    
  2. イベントハンドラは、例えば、以下のような方法で要素にアタッチされます。 on または bind :

    element.on('click', function (event) {
      ...
    });
    
    

スコープ.$destroy()

いつ $scope.$destroy() を介して登録されたリスナーはすべて削除されます。 $on をその$scopeで使用します。

それは ない は、DOM要素や付属のイベントハンドラ(2種類目)を削除します。

つまり $scope.$destroy() ディレクティブのリンク関数の中で、たとえば element.on また、DOM 要素自体も同様です。


要素.remove()

なお remove は jqLite のメソッド(jQuery が AngularjS よりも先にロードされている場合は jQuery のメソッド)であり、標準の DOM Element Object では使用できない。

いつ element.remove() が実行されると、その要素とすべての子要素が DOM から削除され、たとえば element.on .

それは ない 要素に関連付けられた $scope を破棄します。

さらに分かりやすくするために、jQueryのイベントとして $destroy . 要素を削除するサードパーティのjQueryライブラリを使用する場合、または手動で削除する場合、その際にクリーンアップを実行する必要があることがあります。

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


ディレクティブが破壊されたときの対処方法。

これは、ディレクティブがどのように破壊されるかによります。

通常の場合、ディレクティブが破壊されるのは ng-view は現在のビューを変更します。このような場合 ng-view ディレクティブは関連する $scope を破棄し、 その親スコープへの参照をすべて破棄して remove() を指定します。

これは、そのビューが ng-view :

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

element.on('click', function () {
 ...
});

両方のイベントリスナーは自動的に削除されます。

しかし、これらのリスナー内部のコードがメモリリークを引き起こす可能性があることに注意することが重要です。例えば、JSの一般的なメモリリークパターンである circular references .

ビューの変更によってディレクティブが破壊されるという通常のケースであっても、手動でクリーンアップする必要がある場合があります。

例えば、リスナーを $rootScope :

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

scope.$on('$destroy', unregisterFn);

これは $rootScope は、アプリケーションのライフタイム中、決して破棄されません。

スコープが破壊されたときに必要なクリーンアップを自動的に行わない他の pub/sub 実装を使用している場合、あるいはディレクティブがサービスにコールバックを渡している場合も同様です。

もう一つのケースは $interval / $timeout :

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

scope.$on('$destroy', function () {
  $interval.cancel(promise);
});

ディレクティブがイベントハンドラを現在のビューの外の要素などにアタッチしている場合、それらも手動でクリーンアップする必要があります。

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

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

scope.$on('$destroy', function () {
  angular.element(window).off('click', windowClick);
});

これらは、ディレクティブがAngularによって "破壊"されたときにどうするかの例で、例えば、次のようなものでした。 ng-view または ng-if .

DOM要素のライフサイクルを管理するカスタムディレクティブなどがある場合は、もちろんもっと複雑になります。