Behavior of JavaScript addEventListener


Categories: Web
Tags: #JavaScript #Vue

개요

특정 Vue component의 mounted에서 component 내부 특정 DOM 들에 addEventListener로 이벤트 함수를 걸어줌. 이 component는 사용자가 다른 페이지로 넘어가면서 destroyed 됨. 이 때 component 내부 DOM들에 걸려있는 이벤트 함수를 removeEventListener를 통해 명시적으로 지워야 메모리에서 지워지는지(Garbage Collection이 되는지)를 조사. 이 과정에서 찾은 addEventListener 관련된 내용들도 같이 정리.

Vue destroyed 와 EventListener

import { map } from 'lodash-es'

...

{
  data() {
    return {
      selectedEle: null
    }
  },
  methods: {
    scrollEvent(e) {
      /* do something */
    }
  },
  mounted() {
    map(this.$el.getElementsByClassName('select_class_name'), selectedEle => {
      this.selectedEle = selectedEle
      this.selectedEle.addEventListener('scroll', this.scrollEvent)
    })  
  },
  destroyed() {
    this.selectedEle.removeEventListener('scroll', this.scrollEvent)
    this.selectedEle = null
  }
}

위의 코드처럼 destoryedremoveEventListener를 명시적으로 작정해야 메모리에서 수거가 되는지 Vue component에 대해서 찾아봄.

DOM listeners haven’t been removed when vm is destroyed, is this expected? 라는 비슷한 질문이 있었음.

  • 질문 요약: Vue 문서 상에 component가 destroyed 될 때 모든 EventListener들이 자동으로 제거가 된다고 나와있는데 실제로 그렇지 않은 것 같다.
  • 답변 요약: Vue 1.x 에선 그렇지만, Vue 2.x 는 자동으로 제거하지 않는다. Vue 2.x에서는 지원하는 모든 브라우저들이 해당 EventListener들을 Garbage Collection을 해줄 수 있다.

위의 답변에 따르면 destoryedremoveEventListener가 없어도 괜찮을 것 같지만, 일반적인 자바스크립트 관행 상 남기는 것도 괜찮을 것 같다.

observer 형태의 경우, 더 이상 사용되지 않을 때 명시적으로 제거하는 것이 중요합니다. 이는 과거의 경우, 특정 브라우저(Internet Explorer 6 같은)들이 순환 참조를 잘 관리하지 못해 매우 중요한 요소 중 하나였습니다. 현재 대부분의 브라우저들은 observer 객체가 더 이상 사용되지 않으면 명시적으로 제거하지 않더라도 수집합니다. 하지만 이러한 observer들을 명시적으로 제거하는 것이 좋은 관행으로 남아 있습니다.

출처: [번역]자바스크립트에서 메모리 누수의 4가지 형태 on 덕’s IT Story

다수의 동일한 이벤트 리스너가 하나의 DOM Element에 등록될 경우

MDN Web docs의 EventTarget.addEventListener() 내용을 참고하여 다음 case들을 조사

  • Case1 이벤트 함수를 익명함수로 할 경우
    • 익명 함수이기 때문에 이벤트 함수가 중복인 것을 알 수 없어 같은 내용일지라고 중복되서 적용됨.
    • removeEventListener 불가
  • Case2 이벤트 함수를 외부 함수로 선언하여 사용하는 경우
    • 같은 이름을 갖는 이벤트 함수가 중복되서 등록되는 것을 브라우저가 방지
  • Case3 Case 2와 동일하나 이벤트 함수가 loop에서 계속 재할당되는 경우
    • 이벤트 함수가 같은 이름을 갖지만 실제로는 다른 함수이기 때문에 중복 등록을 방지할 수 없음.
    • removeEventListener를 할 경우 맨 마지막 등록된 이벤트 함수만 삭제.

Comments