业务场景
- vue帮我们处理很多数据到视图、数据到事件的处理,其中不乏许多闭包、事件处理,因此比较容易忽视内存溢出的处理。
前言
几种常见的内存溢出
1. 全局变量引起的内存泄漏
2. 闭包引起的内存泄漏
3. dom清空或删除时,事件未清除导致的内存泄漏
4. 被遗忘的计时器或回调函数
全局变量引起的内存泄漏
在window下声明的变量不会被回收,因为window不会被回收.
{
created(){
if (!window.a) {
window.myVue = this;
this.bb = 123;
}
console.log(window.myVue, this, window.myVue === this);
}
}
路由切换之后,此component不会被释放(但是router会重新生成一个此实例,原先的仍旧没有GC),window.myVue指向此对象
闭包引起的内存泄漏
//vue/src/core/vdom/modules/directives.js
function _update(){
// 用来更新vdom,创造,以及销毁vdom
}
// vue/src/core/vdom/helpers/merge-hook.js
function wrappedHook () {
hook.apply(this, arguments)
// important: remove merged hook to ensure its called only once and prevent memory leak
remove(invoker.fns, wrappedHook) //移除调用者的回调
}
// vue/src/core/vdom/helpers/update-listeners
function createFnInvoker(){
//接受一个函数或者一个Function[ ] ,
返回一个调用者函数,并在此函数上保留传入的参数
}
在vue中,这几个函数都有副作用(指除了修改自身的变量,还修改函数外的变量),因此如果传入的变量父对象,就算没有被引用,也无法被GC回收。 因此,这里进行了remove手动释放。
dom清空或删除时,事件未清除导致的内存泄漏
// 一个图片懒惰加载引擎示例
class ImageLazyLoader {
constructor ($photoList) {
$(window).on('scroll', () => {
this.showImage($photoList);
});
}
showImage ($photoList) {
$photoList.each(img => {
// 通过位置判断图片滑出来了就加载
img.src = $(img).attr('data-src');
});
}
}
// 点击分页的时候就初始化一个图片懒惰加载的
$('.page').on('click', function () {
new ImageLazyLoader($('img.photo'));
});
这是一个图片懒加载,photoList仍旧被scroll事件引用,因此该数组并不会被GC。
正确的操作是
clear () {
$(window).off('scroll', this.scrollShow);
}
在点击分页的时候,将此事件进行解绑
vue中,比如你写了一个dialog组件,但是你需要手动在created中监听某个按钮事件button
this.$dialog.$on("button", () => {
console.log(this.xxx)
})
与this.xxx关联的有2者:该语句的上下文,on数组中,当此上下文切换的时候(上下文的dom引用移除),理应被GC的时候,由于此监听没有被移除,因此,并不会被GC。
被遗忘的计时器或回调函数
{
created(){
setInterval(()=> {
console.log(this)
},500)
}
}
调试方法
打开f12进入Memory,选中heap snapshot(堆快照),没进行一次操作就
点一下这个原点,比较两次内存有没有回收
比如上上文的挂载在window全局的组件
找到引用的这个对象,手动GC就好啦