AngularJS - Чи знищує $ знищення слухачів подій?


200

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

Прослуховуючи цю подію, ви можете видалити слухачів подій, які можуть спричинити витік пам'яті. Слухачі, зареєстровані на областях та елементах, автоматично очищаються при їх знищенні, але якщо ви зареєстрували слухача на сервісі або зареєстрували слухача на вузлі DOM, який не видаляється, вам доведеться його очистити самостійно або ви ризикуєте внести витік пам'яті.

Найкраща практика: Директиви повинні прибирати після себе. Ви можете використовувати element.on ('$ знищити', ...) або область. $ On ('$ знищити', ...) для запуску функції очищення при видаленні директиви.

Питання:

Я маю element.on "click", (event) ->внутрішню директиву:

  1. Коли директива буде знищена, чи є якісь посилання на пам'ять, element.onщоб уберегти її від збирання сміття?
  2. У кутовій документації зазначено, що я повинен використовувати обробник, щоб видалити слухачів подій, що $destroyпередаються. У мене було враження, що destroy()віддалені слухачі подій, це не так?

Відповіді:


433

Слухачі подій

По-перше, важливо зрозуміти, що існує два види "слухачів подій":

  1. Слухачі події сфери застосування зареєстровані через $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директива знищить пов'язаний $ range, розділить усі посилання на його батьківський обсяг та викличе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 тощо, звичайно, це стане складніше.


4
'$ rootScope ніколи не знищується протягом життя програми.' : очевидно, як тільки ти думаєш про це. Ось що мені не вистачало.
користувач276648

@tasseKATT Невелике запитання тут: Якщо в одному контролері у нас є кілька $ rootScope. $ on для різних подій, то ми будемо називати $ range. $ on ("$ знищити", ListenerName1); для кожного $ rootScope. $ на різний ??
Яшика Гарг

2
@YashikaGarg Напевно, найпростіше було б мати функцію помічника, яка викликає всіх слухачів. Як і $ range. $ On ('$ знищити'), function () {ListenerName1 (); ListenerName2 (); ...}); Чи є додаткові складності для $ на обробників подій на неізольованих областях? Або ізолювати прилади з двосторонніми прив’язками?
Девід Райс

Навіщо реєструвати слухачів подій на $ rootcope? Я реєструю слухачів подій у $ range, а потім інші контролери виконують $ rootcope.broadcast ('ім'я події') і мої слухачі подій запускаються. Ці слухачі подій в області $, які слухають події програми, все ще будуть автоматично очищені?
Скічан

@Skychan Вибачте, що я пропустив ваш коментар. Це здогад, але люди можуть використовувати саме $rootScopeчерез це: stackoverflow.com/questions/11252780/… Зауважте, що, як йдеться у відповіді вгорі, це було змінено. Так, слухачі подій у звичайному режимі $scopeавтоматично очищатимуться, коли ця область буде знищена.
tasseKATT
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.