AngularJS - Apakah $ menghancurkan menghapus pendengar acara?

200

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

Dengan mendengarkan acara ini, Anda dapat menghapus pendengar acara yang dapat menyebabkan kebocoran memori. Pendengar yang terdaftar di lingkup dan elemen secara otomatis dibersihkan ketika mereka dihancurkan, tetapi jika Anda mendaftarkan pendengar pada layanan, atau mendaftarkan pendengar pada simpul DOM yang tidak dihapus, Anda harus membersihkan sendiri atau Anda berisiko menyebabkan kebocoran memori.

Praktik Terbaik: Arahan harus dibersihkan sendiri. Anda dapat menggunakan element.on ('$ destroy', ...) atau scope. $ On ('$ destroy', ...) untuk menjalankan fungsi pembersihan ketika arahan dihapus.

Pertanyaan:

Saya memiliki element.on "click", (event) ->petunjuk di dalam diri saya:

  1. Ketika arahan dihancurkan, apakah ada referensi memori element.onke agar tidak menjadi sampah yang dikumpulkan?
  2. Dokumentasi sudut menyatakan bahwa saya harus menggunakan penangan untuk menghapus pendengar acara pada $destroyacara yang dipancarkan. Saya mendapat kesan yang destroy()menghilangkan pendengar acara, bukankah ini masalahnya?
dman
sumber

Jawaban:

433

Pendengar acara

Pertama, penting untuk dipahami bahwa ada dua jenis "pendengar acara":

  1. Lingkup acara pendengar terdaftar melalui $on:

    $scope.$on('anEvent', function (event, data) {
      ...
    });
    
  2. Penangan acara dilampirkan ke elemen melalui misalnya onatau bind:

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

$ scope. $ destroy ()

Ketika $scope.$destroy()dijalankan, ini akan menghapus semua pendengar yang terdaftar melalui $on$ scope itu.

Ini tidak akan menghapus elemen DOM atau penangan acara apa pun yang terlampir dari jenis kedua.

Ini berarti bahwa memanggil $scope.$destroy()secara manual dari contoh dalam fungsi tautan direktif tidak akan menghapus handler yang dilampirkan melalui misalnya element.on, atau elemen DOM itu sendiri.


element.remove ()

Perhatikan bahwa removeini adalah metode jqLite (atau metode jQuery jika jQuery dimuat sebelum AngularjS) dan tidak tersedia pada Objek Elemen DOM standar.

Ketika element.remove()dijalankan elemen itu dan semua anak-anaknya akan dihapus dari DOM bersama-sama semua penangan acara akan dilampirkan melalui misalnya element.on.

Itu tidak akan menghancurkan $ scope yang terkait dengan elemen.

Untuk membuatnya lebih membingungkan ada juga acara jQuery yang disebut $destroy. Terkadang saat bekerja dengan pustaka jQuery pihak ketiga yang menghapus elemen, atau jika Anda menghapusnya secara manual, Anda mungkin perlu melakukan pembersihan saat itu terjadi:

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

Apa yang harus dilakukan ketika arahan "dihancurkan"

Ini tergantung pada bagaimana arahan "dihancurkan".

Kasus normal adalah bahwa direktif dihancurkan karena ng-viewmengubah tampilan saat ini. Ketika ini terjadi, ng-viewarahan akan menghancurkan $ scope yang terkait, pisahkan semua referensi ke lingkup induknya dan panggil remove()elemen tersebut.

Ini berarti bahwa jika tampilan tersebut mengandung arahan dengan ini dalam fungsi tautannya ketika dihancurkan oleh ng-view:

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

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

Kedua pendengar acara akan dihapus secara otomatis.

Namun, penting untuk dicatat bahwa kode di dalam pendengar ini masih dapat menyebabkan kebocoran memori, misalnya jika Anda telah mencapai pola kebocoran memori JS yang umum circular references.

Bahkan dalam kasus normal ini direktif dihancurkan karena perubahan pandangan ada beberapa hal yang mungkin perlu Anda bersihkan secara manual.

Misalnya jika Anda telah mendaftarkan pendengar di $rootScope:

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

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

Ini diperlukan karena $rootScopetidak pernah hancur selama masa aplikasi.

Hal yang sama berlaku jika Anda menggunakan implementasi pub / sub lain yang tidak secara otomatis melakukan pembersihan yang diperlukan ketika $ scope dihancurkan, atau jika arahan Anda meneruskan panggilan balik ke layanan.

Situasi lain adalah membatalkan $interval/ $timeout:

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

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

Jika arahan Anda melampirkan penangan acara ke elemen misalnya di luar tampilan saat ini, Anda perlu membersihkannya secara manual juga:

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

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

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

Ini adalah beberapa contoh apa yang harus dilakukan ketika arahan "dihancurkan" oleh Angular, misalnya oleh ng-viewatau ng-if.

Jika Anda memiliki arahan khusus yang mengatur siklus hidup elemen DOM dll. Tentu saja akan menjadi lebih kompleks.

tasseKATT
sumber
4
'$ rootScope tidak pernah dihancurkan selama masa aplikasi.' : jelas sekali Anda memikirkannya. Itu yang saya lewatkan.
user276648
@tasseKATT Sebuah pertanyaan kecil di sini, Jika dalam controller yang sama kita memiliki beberapa $ rootScope. $ on untuk peristiwa yang berbeda, maka kita akan memanggil $ scope. $ on ("$ destroy", ListenerName1); untuk setiap $ rootScope. $ berbeda ??
Yashika Garg
2
@YashikaGarg Mungkin akan lebih mudah jika hanya memiliki fungsi pembantu yang memanggil semua pendengar. Seperti $ scope. $ On ('$ destroy'), function () {ListenerName1 (); ListenerName2 (); ...}); Apakah ada kompleksitas tambahan untuk $ pada event handler pada lingkup non-isolate? Atau mengisolasi ruang lingkup dengan ikatan dua arah?
David Rice
Mengapa mendaftarkan pendengar acara di $ rootscope? Saya mendaftarkan pendengar acara pada $ scope dan kemudian pengendali lain melakukan $ rootscope.broadcast ('eventname') dan pendengar acara saya berjalan. Apakah pendengar acara ini pada $ lingkup yang sedang mendengarkan acara aplikasi masih akan dibersihkan secara otomatis?
Skychan
@Skychan Maaf saya ketinggalan komentar Anda. Ini dugaan, tetapi orang mungkin menggunakannya $rootScopekarena ini: stackoverflow.com/questions/11252780/... Perhatikan bahwa ketika jawaban menyatakan di atas, ini telah diubah. Ya, pendengar acara secara normal $scopeakan otomatis dibersihkan saat ruang lingkup tersebut dihancurkan.
tasseKATT