AngularJS - $ ทำลายเอาผู้ฟังเหตุการณ์หรือไม่


200

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

โดยการฟังเหตุการณ์นี้คุณสามารถลบผู้ฟังเหตุการณ์ที่อาจทำให้เกิดการรั่วไหลของหน่วยความจำ ผู้ฟังที่ลงทะเบียนกับขอบเขตและองค์ประกอบจะถูกล้างข้อมูลโดยอัตโนมัติเมื่อพวกเขาถูกทำลาย แต่ถ้าคุณลงทะเบียนผู้ฟังในบริการหรือลงทะเบียนผู้ฟังบนโหนด DOM ที่ไม่ได้ถูกลบคุณจะต้องล้างมันเองหรือ คุณเสี่ยงต่อการรั่วไหลของหน่วยความจำ

การปฏิบัติที่ดีที่สุด: คำสั่งควรล้างหลังจากตัวเอง คุณสามารถใช้ element.on ('$ destroy', ... ) หรือขอบเขต $ on ('$ destroy', ... ) เพื่อเรียกใช้ฟังก์ชันล้างข้อมูลเมื่อลบคำสั่ง

คำถาม:

ฉันมี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()ด้วยตนเองจากตัวอย่างภายในฟังก์ชั่นลิงค์ของ directive จะไม่ลบ handler ที่แนบมาเช่นelement.onหรือองค์ประกอบ DOM เอง


element.remove ()

โปรดทราบว่าremoveเป็นวิธีการ jqLite (หรือวิธี jQuery หากโหลด jQuery ก่อน AngularjS) และไม่สามารถใช้ได้กับวัตถุองค์ประกอบ DOM มาตรฐาน

เมื่อelement.remove()ถูกดำเนินการองค์ประกอบที่และทุกเด็กของมันจะถูกลบออกจาก DOM element.onร่วมกันทั้งหมดจะจัดการเหตุการณ์ติดผ่านทางตัวอย่างเช่น

มันจะไม่ทำลายขอบเขต $ ที่เกี่ยวข้องกับองค์ประกอบ

$destroyที่จะทำให้มันมากขึ้นทำให้เกิดความสับสนนอกจากนี้ยังมีเหตุการณ์ที่เรียกว่า jQuery บางครั้งเมื่อทำงานกับไลบรารี jQuery บุคคลที่สามที่ลบองค์ประกอบออกหรือถ้าคุณลบด้วยตนเองคุณอาจต้องล้างข้อมูลเมื่อเกิดเหตุการณ์เช่นนี้:

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

จะทำอย่างไรเมื่อคำสั่ง "ถูกทำลาย"

ขึ้นอยู่กับว่าคำสั่งนั้นถูก "ทำลาย" อย่างไร

กรณีปกติคือคำสั่งถูกทำลายเพราะng-viewเปลี่ยนมุมมองปัจจุบัน เมื่อสิ่งนี้เกิดขึ้นng-viewคำสั่งจะทำลายขอบเขต $ ที่เกี่ยวข้องตัดการอ้างอิงทั้งหมดไปยังขอบเขตหลักและเรียก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-viewng-ifหรือ

หากคุณมีคำสั่งที่กำหนดเองที่จัดการวงจรชีวิตขององค์ประกอบ DOM ฯลฯ แน่นอนว่ามันจะซับซ้อนมากขึ้น


4
'$ rootScope ไม่เคยถูกทำลายในช่วงอายุการใช้งานของแอปพลิเคชัน' ชัดเจนเมื่อคุณคิดถึงมัน นั่นคือสิ่งที่ฉันขาดหายไป
user276648

@tasseKATT คำถามเล็ก ๆ ที่นี่ถ้าในคอนโทรลเลอร์เดียวกันเรามี $ rootScope หลายอัน $ on สำหรับเหตุการณ์ต่าง ๆ เราจะเรียก $ scope $ on ("$ destroy", ListenerName1); สำหรับแต่ละ $ rootScope $ แตกต่างกันหรือไม่?
Yashika Garg

2
@YashikaGarg มันอาจจะง่ายที่สุดที่จะมีฟังก์ชั่นตัวช่วยที่เรียกผู้ฟังทั้งหมด เช่นเดียวกับ $ scope $ on ('$ destroy'), function () {ListenerName1 (); ListenerName2 (); ... }); มีความซับซ้อนเพิ่มเติมสำหรับ $ ในตัวจัดการเหตุการณ์ในขอบเขตที่ไม่แยกหรือไม่ หรือแยกขอบเขตด้วยการผูกสองทาง?
David Rice

ทำไมต้องลงทะเบียนผู้ฟังเหตุการณ์ใน $ Rootcope ฉันลงทะเบียนตัวรับฟังเหตุการณ์ในขอบเขต $ จากนั้นผู้ควบคุมรายอื่นก็ทำ $ Rootcope.broadcast ('eventname') และผู้ฟังเหตุการณ์ของฉันก็ทำงาน ฟังเหตุการณ์เหล่านี้ในขอบเขต $ ที่กำลังฟังเหตุการณ์แอปพลิเคชันจะยังคงถูกล้างอัตโนมัติหรือไม่
Skychan

@Skychan ขอโทษฉันพลาดความคิดเห็นของคุณ นี่คือการเดา แต่ผู้คนอาจใช้$rootScopeเพราะสิ่งนี้: stackoverflow.com/questions/11252780/…โปรดทราบว่าเนื่องจากคำตอบระบุไว้ที่ด้านบนสุดสิ่งนี้จึงเปลี่ยนไป ใช่ผู้ฟังเหตุการณ์ตามปกติ$scopeจะถูกล้างอัตโนมัติเมื่อขอบเขตนั้นถูกทำลาย
tasseKATT
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.