AngularJS - $ destroy удаляет прослушиватели событий?

200

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

Прослушивая это событие, вы можете удалить прослушиватели событий, которые могут вызвать утечки памяти. Прослушиватели, зарегистрированные для областей и элементов, автоматически очищаются при их уничтожении, но если вы зарегистрировали прослушиватель в службе или зарегистрировали прослушиватель на узле DOM, который не был удален, вам придется очистить его самостоятельно или вы рискуете вызвать утечку памяти.

Лучшая практика: директивы должны убирать за собой. Вы можете использовать element.on ('$ destroy', ...) или scope. $ On ('$ destroy', ...), чтобы запустить функцию очистки после удаления директивы.

Вопрос:

У меня есть element.on "click", (event) ->внутренняя директива:

  1. Когда директива уничтожена, существуют ли какие-либо ссылки на память, чтобы element.onпредотвратить ее сборку?
  2. Документация Angular гласит, что я должен использовать обработчик для удаления прослушивателей событий на $destroyвыпущенном событии. У меня сложилось впечатление, что destroy()удалены слушатели событий, разве это не так?
ГАУ
источник

Ответы:

433

Слушатели событий

Прежде всего, важно понимать, что существует два типа «слушателей событий»:

  1. Слушатели событий Scope зарегистрированы через $on:

    $scope.$on('anEvent', function (event, data) {
      ...
    });
  2. Обработчики событий, прикрепленные к элементам, например, onили bind:

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

$ Сфера. $ Уничтожить ()

Когда $scope.$destroy()выполняется, он удалит всех слушателей, зарегистрированных через $onв этой области $.

Он не будет удалять элементы DOM или любые прикрепленные обработчики событий второго рода.

Это означает, что вызов $scope.$destroy()вручную из примера в функции ссылки директивы не приведет к удалению обработчика, подключенного, например element.on, через сам элемент DOM.


element.remove ()

Обратите внимание, что removeэто метод jqLite (или метод jQuery, если jQuery загружен до AngularjS) и недоступен в стандартном объектном элементе DOM.

Когда element.remove()выполняется этот элемент и все его дочерние элементы будут удалены из DOM вместе, все обработчики событий будут присоединены, например, через element.on.

Это не уничтожит область видимости $, связанную с элементом.

Чтобы сделать это еще более запутанным, существует также событие 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, которая автоматически не выполняет необходимую очистку при уничтожении $ scope или если ваша директива передает обратные вызовы сервисам.

Другая ситуация будет отменить $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 и т. Д., Это, конечно, станет более сложным.

tasseKATT
источник
4
'$ rootScope никогда не уничтожается в течение жизни приложения.' : очевидно, если подумать. Вот чего мне не хватало.
user276648
@tasseKATT Небольшой вопрос здесь: если в одном контроллере у нас есть несколько $ rootScope. $ on для разных событий, то мы будем называть $ scope. $ on ("$ destroy", ListenerName1); за каждый $ rootScope. $ по разному ??
Яшика Гарг
2
@YashikaGarg Вероятно, было бы проще всего иметь вспомогательную функцию, которая вызывает всех слушателей. Как и $ scope. $ On ('$ destroy'), function () {ListenerName1 (); ListenerName2 (); ...}); Есть ли какая-либо дополнительная сложность для обработчиков событий $ on в неизолированных областях? Или изолировать области с двухсторонним связыванием?
Дэвид Райс
Зачем регистрировать прослушиватели событий на $ rootscope? Я регистрирую прослушиватели событий в $ scope, а затем другие контроллеры выполняют $ rootscope.broadcast ('eventname'), и мои прослушиватели событий запускаются. Эти прослушиватели событий в $ scope, которые прослушивают события приложения, все еще будут автоматически очищаться?
Skychan
@Skychan Извините, я пропустил ваш комментарий. Это предположение, но люди могут использовать $rootScopeиз-за этого: stackoverflow.com/questions/11252780/… Обратите внимание, что, как указано в ответе вверху, это было изменено. Да, обычные прослушиватели событий $scopeбудут автоматически очищаться при уничтожении этой области.
tasseKATT