web-dev-qa-db-fra.com

AngularJS - $ destroy supprime-t-il les écouteurs d'événements?

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

En écoutant cet événement, vous pouvez supprimer les écouteurs d'événements susceptibles de provoquer des fuites de mémoire. Les écouteurs enregistrés dans les portées et les éléments sont automatiquement nettoyés lorsqu'ils sont détruits, mais si vous avez enregistré un auditeur sur un service ou si vous avez enregistré un auditeur sur un nœud DOM qui n'est pas supprimé, vous devrez le nettoyer vous-même ou vous risquez d'introduire une fuite de mémoire.

Meilleure pratique: les directives doivent être nettoyées après elles-mêmes. Vous pouvez utiliser element.on ('$ destroy', ...) ou scope. $ On ('$ destroy', ...) pour exécuter une fonction de nettoyage lorsque la directive est supprimée.

Question:

J'ai un element.on "click", (event) -> dans ma directive:

  1. Lorsque la directive est détruite, y a-t-il des références de mémoire au element.on pour l'empêcher d'être nettoyé?
  2. La documentation angulaire indique que je devrais utiliser un gestionnaire pour supprimer les écouteurs d'événements sur l'événement $destroy émis. J'avais l'impression que destroy() a supprimé les écouteurs d'événement, n'est-ce pas le cas?
194
dman

Auditeurs d'événements

Tout d'abord, il est important de comprendre qu'il existe deux types d '"auditeurs d'événements":

  1. Auditeurs d’événement Scope enregistrés via _$on_:

    _$scope.$on('anEvent', function (event, data) {
      ...
    });
    _
  2. Les gestionnaires d’événements attachés aux éléments via par exemple on ou bind:

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

$ scope. $ destroy ()

Lorsque $scope.$destroy() est exécuté, tous les écouteurs enregistrés via _$on_ seront supprimés de cette étendue $.

Cela va not supprimer les éléments du DOM ou tous les gestionnaires d’événements attachés du second type.

Cela signifie que l'appel de $scope.$destroy() manuellement à partir d'un exemple dans la fonction link d'une directive ne supprimera pas un gestionnaire attaché via, par exemple, _element.on_, ni l'élément DOM lui-même.


element.remove ()

Notez que remove est une méthode jqLite (ou une méthode jQuery si jQuery est chargée avant AngularjS) et n'est pas disponible sur un objet d'élément DOM standard

Lorsque element.remove() est exécuté, cet élément et tous ses enfants seront supprimés du DOM. Tous les gestionnaires d'événements attachés via, par exemple, _element.on_.

Il va not détruire le $ scope associé à l'élément.

Pour rendre cela plus confus, il existe également un événement jQuery appelé _$destroy_. Parfois, lorsque vous travaillez avec des bibliothèques tierces jQuery qui suppriment des éléments, ou si vous les supprimez manuellement, vous devrez peut-être effectuer un nettoyage lorsque cela se produit:

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

Que faire lorsqu'une directive est "détruite"

Cela dépend de la manière dont la directive est "détruite".

En règle générale, une directive est détruite car _ng-view_ modifie la vue actuelle. Lorsque cela se produit, la directive _ng-view_ détruira la portée $ associée, coupera toutes les références à sa portée parente et appellera remove() sur l'élément.

Cela signifie que si cette vue contient une directive avec cela dans sa fonction de liaison lorsqu'elle est détruite par _ng-view_:

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

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

Les deux écouteurs d'événement seront automatiquement supprimés.

Cependant, il est important de noter que le code à l'intérieur de ces écouteurs peut toujours causer des fuites de mémoire, par exemple si vous avez atteint le modèle de fuite de mémoire JS commun circular references.

Même dans le cas normal où une directive est détruite en raison d'un changement de vue, il vous faudra peut-être nettoyer certaines choses.

Par exemple, si vous avez enregistré un écouteur sur _$rootScope_:

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

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

Cela est nécessaire car _$rootScope_ n'est jamais détruit pendant la durée de vie de l'application.

Il en va de même si vous utilisez une autre implémentation pub/sub qui n'effectue pas automatiquement le nettoyage nécessaire lorsque $ scope est détruite, ou si votre directive transmet des rappels aux services.

Une autre situation serait d'annuler _$interval_/_$timeout_:

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

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

Si votre directive attache des gestionnaires d'événements à des éléments, par exemple en dehors de la vue actuelle, vous devez également les nettoyer manuellement:

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

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

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

Voici quelques exemples de mesures à prendre lorsque les directives sont "détruites" par Angular, par exemple par _ng-view_ ou _ng-if_.

Si vous avez des directives personnalisées qui gèrent le cycle de vie des éléments DOM, etc., cela deviendra évidemment plus complexe.

428
tasseKATT