AngularJS - $ destroy rimuove i listener di eventi?


200

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

Ascoltando questo evento, è possibile rimuovere i listener di eventi che potrebbero causare perdite di memoria. I listener registrati in ambiti ed elementi vengono ripuliti automaticamente quando vengono distrutti, ma se hai registrato un listener in un servizio o hai registrato un listener in un nodo DOM che non viene eliminato, dovrai ripulirlo da solo o si rischia di introdurre una perdita di memoria.

Best practice: le direttive dovrebbero ripulire dopo se stesse. È possibile utilizzare element.on ('$ destroy', ...) o scope. $ On ('$ destroy', ...) per eseguire una funzione di pulizia quando viene rimossa la direttiva.

Domanda:

Ho un element.on "click", (event) ->dentro la mia direttiva:

  1. Quando la direttiva viene distrutta, ci sono riferimenti alla memoria per element.onimpedirne la raccolta?
  2. La documentazione angolare afferma che dovrei usare un gestore per rimuovere i listener di eventi $destroysull'evento emesso. Avevo l'impressione di aver destroy()rimosso i listener di eventi, non è così?

Risposte:


433

Ascoltatori di eventi

Prima di tutto è importante capire che esistono due tipi di "ascoltatori di eventi":

  1. Ascoltatori di eventi Scope registrati tramite $on:

    $scope.$on('anEvent', function (event, data) {
      ...
    });
  2. Gestori di eventi collegati ad elementi tramite ad esempio ono bind:

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

$ Portata. $ Destroy ()

Quando $scope.$destroy()viene eseguito rimuoverà tutti gli ascoltatori registrati tramite$on quel $ scope.

Sarà Non rimuovere elementi DOM o qualsiasi gestori eventi allegati del secondo tipo.

Ciò significa che la chiamata $scope.$destroy()manuale dall'esempio all'interno della funzione di collegamento di una direttiva non rimuoverà un gestore collegato tramite, ad esempio element.on, né l'elemento DOM stesso.


element.remove ()

Nota che remove è un metodo jqLite (o un metodo jQuery se jQuery è caricato prima di AngularjS) e non è disponibile su un oggetto elemento DOM standard.

Quando element.remove()viene eseguito quell'elemento e tutti i suoi figli verranno rimossi dal DOM insieme, ad esempio, tutti i gestori di eventi collegati tramiteelement.on .

Esso non distruggere il $ ambito associato all'elemento.

Per renderlo più confuso c'è anche un evento jQuery chiamato $destroy. A volte, quando si lavora con librerie jQuery di terze parti che rimuovono elementi o se si rimuovono manualmente, potrebbe essere necessario eseguire la pulizia quando ciò accade:

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

Cosa fare quando una direttiva viene "distrutta"

Questo dipende da come la direttiva viene "distrutta".

Un caso normale è che una direttiva viene distrutta perché ng-viewcambia la vista corrente. Quando ciò accade, la ng-viewdirettiva distrugge l'ambito $ associato, recide tutti i riferimenti al suo ambito padre e chiamaremove() l'elemento.

Ciò significa che se quella vista contiene una direttiva con questa nella sua funzione di collegamento quando viene distrutta da ng-view:

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

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

Entrambi i listener di eventi verranno rimossi automaticamente.

Tuttavia, è importante notare che il codice all'interno di questi listener può comunque causare perdite di memoria, ad esempio se è stato raggiunto il modello di perdita di memoria JS comune circular references.

Anche in questo caso normale in cui una direttiva viene distrutta a causa di una modifica della vista, è possibile che sia necessario ripulire manualmente.

Ad esempio se hai registrato un ascoltatore su $rootScope:

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

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

Questo è necessario da allora $rootScope non viene mai distrutto durante la vita dell'applicazione.

Lo stesso vale se si utilizza un'altra pub / sub implementazione che non esegue automaticamente la pulizia necessaria quando viene distrutto $ scope o se la direttiva trasmette i callback ai servizi.

Un'altra situazione sarebbe quella di annullare $interval/ $timeout:

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

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

Se la direttiva collega gestori di eventi ad elementi esterni alla vista corrente, è necessario ripulire manualmente anche quelli:

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

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

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

Questi sono stati alcuni esempi di cosa fare quando le direttive vengono "distrutte" da Angular, ad esempio da ng-viewo ng-if.

Se hai direttive personalizzate che gestiscono il ciclo di vita degli elementi DOM ecc., Diventerà ovviamente più complesso.


4
'$ rootScope non viene mai distrutto durante il ciclo di vita dell'applicazione.' : ovvio una volta che ci pensi. Questo è quello che mi mancava.
user276648

@tasseKATT Una piccola domanda qui, se nello stesso controller abbiamo più $ rootScope. $ on per eventi diversi, allora dovremo chiamare $ scope. $ on ("$ destroy", ListenerName1); per ogni $ rootScope. $ in modo diverso ??
Yashika Garg,

2
@YashikaGarg Probabilmente sarebbe più semplice avere una funzione di supporto che chiama tutti gli ascoltatori. Come $ scope. $ On ('$ destroy'), function () {ListenerName1 (); ListenerName2 (); ...}); C'è qualche ulteriore complessità per $ sui gestori di eventi su ambiti non isolati? O isolare gli ambiti con attacchi bidirezionali?
David Rice,

Perché registrare i listener di eventi su $ rootscope? Registro i listener di eventi su $ scope e poi altri controller eseguono $ rootscope.broadcast ('eventname') e i miei listener di eventi vengono eseguiti. Questi listener di eventi su $ scope che stanno ascoltando gli eventi dell'applicazione verranno ancora puliti automaticamente?
Skychan,

@Skychan Mi dispiace aver perso il tuo commento. Questa è una supposizione, ma le persone potrebbero usare $rootScopeper questo: stackoverflow.com/questions/11252780/… Nota che, come indicato nella risposta in alto, questo è stato modificato. Sì, i listener di eventi normalmente $scopevengono puliti automaticamente quando tale ambito viene distrutto.
tasseKATT
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.