Vue.js 3 卸载和内存泄漏
Vue.js 3 unmount and memory leak
为此 post 我创建了一个简单的示例:
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 控制台中查找内存泄漏:
出于调试原因,创建的应用程序存储在 testClass 的 this.vueApp 中,因此我们可以轻松找到对象 ID。只需按照以下步骤
- 运行代码
- 点击第一个按钮(.mount Vue 应用程序)。将出现“Vue mounted”文本
- 打开 chrome 控制台并切换到内存选项卡
- 拍摄堆快照
- 点击第二个按钮(.unmount Vue app)。 “Vue 安装”文本将消失
- 返回“内存”选项卡,单击“收集垃圾”(带垃圾箱的图标)
- 拍摄第二个堆快照
- 切换到第一个拍摄的快照并过滤“testClass”。 (您只会看到一个结果)。打开它,找到 public 属性 “vueApp”。在它旁边,您会找到存储在此 属性 中的对象的 @ID(例如 @567005)
- 切换到第二个快照并按 CTRL+F(查找)。搜索相同的@ID(例如@567005)。这是内存泄漏:使用 Vue.createApp 创建的对象仍在内存中!它不是用垃圾收集器收集的,因为某些东西仍然指向这个对象
如何解决此内存泄漏
我找到的唯一解决方案是从 DOM 中删除 DIV#appDiv
(删除此元素的代码在 myTest.unmount()
方法中进行了注释)。之后,再次调用垃圾收集器将从内存中删除该对象。
还有其他解决办法吗?
为什么这个(大)问题
在多屏的大型应用中,创建和删除整个应用是唯一的方法,如何节省内存(脚本只是加载实际页面的代码,当用户想要另一个页面时,实际页面被销毁并新页面加载,然后创建新的 Vue 应用程序)
你也不能通过创建动态组件来解决这个问题,因为 Vue3 删除了(我认为这是一个大错误) $destroy 方法,所以当你为新屏幕创建新组件时,旧组件将保留在永远的记忆。
Vue router 不会解决这个问题,因为 Vue router 在启动时加载所有页面,这在大型应用程序中是不可接受的,因为网络带宽将是巨大的(为一个应用程序加载兆字节的代码是错误的)
已在 VUE 3.0.6 中修复
VUE js 版本 3.0.6 修复了这个问题
为此 post 我创建了一个简单的示例: 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 控制台中查找内存泄漏:
出于调试原因,创建的应用程序存储在 testClass 的 this.vueApp 中,因此我们可以轻松找到对象 ID。只需按照以下步骤
- 运行代码
- 点击第一个按钮(.mount Vue 应用程序)。将出现“Vue mounted”文本
- 打开 chrome 控制台并切换到内存选项卡
- 拍摄堆快照
- 点击第二个按钮(.unmount Vue app)。 “Vue 安装”文本将消失
- 返回“内存”选项卡,单击“收集垃圾”(带垃圾箱的图标)
- 拍摄第二个堆快照
- 切换到第一个拍摄的快照并过滤“testClass”。 (您只会看到一个结果)。打开它,找到 public 属性 “vueApp”。在它旁边,您会找到存储在此 属性 中的对象的 @ID(例如 @567005)
- 切换到第二个快照并按 CTRL+F(查找)。搜索相同的@ID(例如@567005)。这是内存泄漏:使用 Vue.createApp 创建的对象仍在内存中!它不是用垃圾收集器收集的,因为某些东西仍然指向这个对象
如何解决此内存泄漏
我找到的唯一解决方案是从 DOM 中删除 DIV#appDiv
(删除此元素的代码在 myTest.unmount()
方法中进行了注释)。之后,再次调用垃圾收集器将从内存中删除该对象。
还有其他解决办法吗?
为什么这个(大)问题
在多屏的大型应用中,创建和删除整个应用是唯一的方法,如何节省内存(脚本只是加载实际页面的代码,当用户想要另一个页面时,实际页面被销毁并新页面加载,然后创建新的 Vue 应用程序)
你也不能通过创建动态组件来解决这个问题,因为 Vue3 删除了(我认为这是一个大错误) $destroy 方法,所以当你为新屏幕创建新组件时,旧组件将保留在永远的记忆。
Vue router 不会解决这个问题,因为 Vue router 在启动时加载所有页面,这在大型应用程序中是不可接受的,因为网络带宽将是巨大的(为一个应用程序加载兆字节的代码是错误的)
已在 VUE 3.0.6 中修复
VUE js 版本 3.0.6 修复了这个问题