#vue.js #memory-leaks #vuejs3
#vue.js #утечки памяти #vuejs3
Вопрос:
Для целей этого поста я создал простой пример: http://wagoon.demoeshop.net/test-remove-vue.html
В этом примере вы найдете две кнопки.
- Первая кнопка создает
DIV
элемент, затем создает приложение Vue и монтирует его в этоdiv
- Вторая кнопка отключит приложение
Пример кода
В моем примере вы найдете две кнопки
<button type="button" onclick="myTest.mount()">.mount()</button>
<button type="button" onclick="myTest.unmount()">.unmount()</button>
Vue.js 3 входит в комплект
<script src="https://unpkg.com/vue@next"></script>
Весь код javascript заключен в функцию testClass()
по причинам отладки:
function testClass(){
// vueApp is public just for debugging reasons
this.vueApp = null;
// creates DIV with id #appDiv and apends it to <body>
function createDiv(){
var div = document.createElement('div');
div.id = "appDiv";
document.body.append(div);
}
// creates new Vue app and mounts it to #appDiv
this.mount = function(){
createDiv();
this.vueApp = Vue.createApp({"template":"Vue mounted"});
this.vueApp.mount('#appDiv');
}
// unmounts Vue app
this.unmount = function(){
// MEMORY LEAK HERE:
this.vueApp.unmount('#appDiv'); // this line should mark vueApp as collectable for garbage collector,it's not
this.vueApp = null; // event this line does not help
// SOLUTION: only removing app taget element from DOM is marking object created with Vue.createApp()
// as collectable by garbage collector.
// document.querySelector('#appDiv').remove();
}
}
myTest = new testClass();
Как найти утечку памяти в консоли Google Chrome:
По причине отладки созданное приложение сохраняется в this.vueApp в TestClass, чтобы мы могли легко найти идентификатор объекта. Просто выполните следующие действия
- Запустите код
- нажмите на первую кнопку (.смонтировать приложение Vue). Появится текст «Vue смонтирован»
- откройте консоль Chrome и перейдите на вкладку Память
- Сделайте снимок кучи
- нажмите на вторую кнопку (.размонтировать приложение Vue). Текст «Vue смонтирован» исчезнет
- вернувшись на вкладку «Память», нажмите «Собрать мусор» (значок с мусорной корзиной).
- Сделайте второй снимок кучи
- Переключитесь на первый сделанный снимок и отфильтруйте «Тестовый класс». (вы увидите только один результат). Откройте его и найдите общедоступное свойство «vueApp». Рядом с ним вы найдете @ID объекта, хранящегося в этом свойстве (например, @567005)
- Переключитесь на второй снимок и нажмите CTRL F (найти). Найдите тот же @ID (например, @567005). Вот утечка памяти: объект, созданный с помощью Vue.createApp, все еще находится в памяти! Он НЕ был собран с помощью сборщика мусора, потому что что-то все еще указывает на этот объект
Как устранить эту утечку памяти
Единственное решение, которое я нашел, — это удаление DIV#appDiv
из DOM (код для удаления этого элемента прокомментирован в myTest.unmount()
методе). После этого повторный вызов сборщика мусора удалит этот объект из памяти.
Есть ли какое-либо другое решение?
Почему это (большая) проблема
В больших приложениях с несколькими экранами создание и удаление всего приложения — единственный способ сэкономить память (скрипт просто загружает код для фактической страницы, и когда пользователь хочет другую страницу, фактическая страница уничтожается и загружается новая страница, затем создается новое приложение Vue)
Вы также не можете решить эту проблему с помощью создания динамических компонентов, потому что Vue3 удалил (и я считаю это большой ошибкой) метод $destroy, поэтому, когда вы создаете новый компонент для нового экрана, старый компонент останется в памяти навсегда.
Маршрутизатор Vue не решит эту проблему, потому что маршрутизатор Vue загружает все страницы при запуске, а это неприемлемо в больших приложениях, потому что пропускная способность сети будет огромной (мегабайты кода, загруженные только для одного приложения, просто неверны)
Комментарии:
1. «Маршрутизатор Vue загружает все страницы при запуске» — router.vuejs.org/guide/advanced/lazy-loading.html
2. Маршрутизатор использует import (), и это можно сделать в стиле «отложенной загрузки» (загрузка по требованию). Но проблема не в этом. Проблема в том, что если вы используете маршрутизатор, вы НЕ можете удалить (или «выгрузить») модуль импорта из памяти. Но этот пост не о маршрутизаторе, а о методе unmount ()
3. тогда стоит создавать проблемы в vue3 github: github.com/vuejs/vue-next/issues
4. спасибо, я создал новый отчет github.com/vuejs/vue-next/issues/2907
5. Похоже, нам не нужно отключать приложение vue, чтобы оно собирало мусор? Итак, простого удаления корневого элемента dom достаточно, чтобы пометить его для сборки мусора? Или я это неправильно понял. Я собираюсь удалить многие элементы dom, содержащие смонтированные экземпляры приложений vue, а затем повторно смонтировать новые, поэтому было бы здорово узнать о влиянии этого на память.
Ответ №1:
Исправлено в VUE 3.0.6
VUE js версии 3.0.6 исправил эту проблему
Комментарии:
1. Помогло бы, если бы вы расширили свой ответ относительно того, как он теперь исправлен, или предоставили ссылку, поскольку OP написал очень подробное объяснение, которое можно считать «исправленным» несколькими способами.
2. Подробное объяснение находится в сообщении GitHub здесь: github.com/vuejs/vue-next/issues/2907 и github.com/vuejs/vue-next/pull/2909