Удаление прослушивателя событий в методе жизненного цикла beforeDestroy не работает, если элемент был удален с помощью v-if в родительском компоненте

В основном я использую библиотеку datepicker, которая не генерирует события фокуса или размытия, поэтому мне нужно добавить их вручную, но у меня возникают проблемы с удалением прослушивателей событий из-за восходящего потока v-if.

У меня так получилось:

<datepicker :id="datePickerId">

...

computed: {
    datePickerId() { return `date-picker-${this.name}`; },
},

mounted() {
    document.getElementById(this.datePickerId).addEventListener('focus', this.handleFocus);
    document.getElementById(this.datePickerId).addEventListener('blur', this.handleBlur);
},

beforeDestroy() {
    document.getElementById(this.datePickerId).removeEventListener('focus', this.handleFocus);
    document.getElementById(this.datePickerId).removeEventListener('blur', this.handleBlur);
},

Это отлично работает до тех пор, пока мой компонент не будет условно отрисован через v-if, например:

<my-datepicker v-if="isDisplayed">

Я исследовал его, и проблема, похоже, заключается в том, что элемент ввода удален из DOM из-за v-if, но экземпляр Vue все еще существует, поэтому метод жизненного цикла beforeDestroy вызывается после того, как элемент уже исчез.

Есть ли способ предотвратить утечку памяти, не считая простого пропуска шага очистки и использования современной сборки мусора в браузере?

Мне очень интересно узнать больше о том, как получить больший контроль над тем, что происходит в то время, когда v-if становится ложным. Об этом «неподходящем моменте времени» не так много статей / выпусков / билетов, поэтому, если у кого-то есть какие-либо связанные мнения, пожалуйста, не стесняйтесь добавлять их.

Есть ли другой способ подключиться к событиям фокуса / размытия ввода сторонней библиотеки?

[Предупреждение Vue]: Ошибка в обработчике beforeDestroy: «TypeError: невозможно прочитать свойство removeEventListener, равное null»


person agm1984    schedule 16.03.2019    source источник


Ответы (1)


Я смог исправить это после краткого указания Криса Фрица из команды Vue. Он сказал, посмотрите на делегирование событий, поэтому я нашел решение, в котором используются focusin и focusout.

Ранее я читал, что современные браузеры автоматически выполняют прослушиватели событий сборки мусора при удалении элементов DOM. Утечки памяти на самом деле являются проблемой только для IE7, и Крис сказал, что сам Vue даже не поддерживает IE7, поэтому существует альтернативное решение, в котором вы можете просто не удалять прослушиватели событий. Жизненный цикл beforeDestroy может быть удален.

Однако я предпочитаю это решение с делегированием событий:

Вот разметка, которая решает проблему:

<div @focusin="handleFocus" @focusout="handleBlur">
    <datepicker
        @input="handleChange"
        :name="name"
    ></datepicker>
</div>

...

methods: {
    handleFocus() { console.log('focus in'); this.$emit('focus'); },
    handleBlur() { console.log('focus out'); this.$emit('blur'); },
},

Вы можете прочитать далее в документации MDN:

https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event

https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event

Кроме того, проблемная библиотека datepicker называется vuejs-datepicker, и она довольно хороша, если не принимать во внимание события focus или blur.

person agm1984    schedule 16.03.2019