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 documentation에는 생성 $destroy된 이벤트 에서 이벤트 리스너를 제거하기 위해 핸들러를 사용해야한다고 명시되어 있습니다 . destroy()이벤트 리스너 를 제거 했다는 인상을 받았습니다 . 그렇지 않습니까?

답변:


433

이벤트 리스너

먼저 두 가지 종류의 "이벤트 리스너"가 있다는 것을 이해하는 것이 중요합니다.

  1. 다음을 통해 등록 된 범위 이벤트 리스너 $on:

    $scope.$on('anEvent', function (event, data) {
      ...
    });
  2. 예를 들어를 통해 요소에 부착 된 이벤트 핸들러 on또는 bind:

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

$ scope. $ destroy ()

$scope.$destroy()실행 되면 $on해당 $ scope 를 통해 등록 된 모든 리스너가 제거 됩니다.

그것은 것입니다 하지 DOM 요소 또는 두 번째 종류의 연결된 이벤트 핸들러를 제거합니다.

$scope.$destroy(), 지시문의 링크 함수 내에서 예제에서 수동으로 호출 하면 예를 들어 연결된 처리기 element.on나 DOM 요소 자체가 제거되지 않습니다 .


element.remove ()

remove(JQuery와 AngularJS와 이전에로드 된 경우 또는 jQuery를 방법) 및 표준 DOM 요소 개체에 사용할 수없는 jqLite 방법입니다.

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 () {
 ...
});

두 이벤트 리스너가 자동으로 제거됩니다.

그러나 공통 리스너 메모리 누수 패턴을 달성 한 경우와 같이 이러한 리스너 내부의 코드로 인해 메모리 누수가 여전히 발생할 수 있습니다 circular references.

뷰 변경으로 인해 지시문이 파괴되는 정상적인 경우에도 수동으로 정리해야 할 사항이 있습니다.

예를 들어 리스너를 등록한 경우 $rootScope:

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

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

이것은 $rootScope응용 프로그램 수명 동안 파괴되지 않기 때문에 필요 합니다.

$ scope가 파괴 될 때 필요한 정리를 자동으로 수행하지 않는 다른 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는 응용 프로그램 수명 동안 절대 파괴되지 않습니다.' : 일단 생각하면 분명합니다. 그것이 내가 놓친 것입니다.
user276648

@tasseKATT 여기에 작은 질문이 있습니다. 같은 컨트롤러에 다른 이벤트에 대해 여러 $ rootScope. $ on이 있으면 $ scope. $ on ( "$ destroy", ListenerName1); 각 $ rootScope. $에 대해 다르게?
Yashika Garg

2
@YashikaGarg 아마도 모든 리스너를 호출하는 도우미 함수를 갖는 것이 가장 쉬울 것입니다. $ scope. $ on ( '$ destroy')와 같이 function () {ListenerName1 (); ListenerName2 (); ...}); 비 분리 범위에서 $ on 이벤트 핸들러에 대한 추가 복잡성이 있습니까? 또는 양방향 바인딩으로 스코프를 분리합니까?
David Rice

$ rootscope에 이벤트 리스너를 등록해야하는 이유는 무엇입니까? $ scope에 이벤트 리스너를 등록하면 다른 컨트롤러가 $ rootscope.broadcast ( 'eventname')을 수행하고 이벤트 리스너가 실행됩니다. 응용 프로그램 이벤트를 수신하는 $ scope의 이러한 이벤트 리스너가 여전히 자동 정리됩니까?
Skychan

@Skychan 죄송합니다 귀하의 의견을 그리워. 이것은 추측이지만, 사람들이 사용할 수있는 $rootScope이 때문에 : stackoverflow.com/questions/11252780/... 상단의 응답 상태로,이 변경되었음을 참고. 예. 정상 $scope범위의 이벤트 리스너 는 해당 범위가 삭제되면 자동으로 정리됩니다.
tasseKATT
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.