vue使用中的内存泄漏[推荐]
今天看到一篇关于js使用中内存泄漏的文章,以及在chrom浏览器中查看内存泄漏的方法,所以决定保留。这篇文章只截取了我认为重要的部分。如果你喜欢原文,请点击文章底部的原文链接。
什么是内存泄漏?内存泄漏意味着新的内存被保存,但不能被释放或垃圾收集。new创建一个对象后,它应用于占用一堆内存。当这个对象的指针被设置为null或离开作用域时,它就被销毁了。那么这个内存中就没有人引用它了,它会在JS中被自动垃圾回收。但是如果这个对象指针没有设置为null,并且在代码中没有办法得到这个对象指针,就会导致无法释放它所指向的内存,也就是发生内存泄漏。为什么不能在代码中得到这个对象指针?举个例子:
//模块date.jslet日期=null导出默认值{ init(){ Date=new Date();}}//main.jsimport日期来自“date . js”;date . init();main.js初始化日期后,变量date会存在一段时间,直到你关闭页面,因为日期的引用在另一个模块中,可以理解为模块是一个外界不可见的闭包。所以,如果你想让这个日期对象一直存在,需要一直使用,那么没有问题,但是如果你想用一次,就会有问题,而且这个对象一直在内存中没有释放。
另一个隐藏且常见的内存泄漏是事件绑定,它形成一个闭包,并导致一些变量一直存在。如下例所示:
//图片惰性加载引擎示例类imagelaziloader { constructor($ photo list){ $(window)。on ('scroll ',()={this。show image($ photo list);});} show image($ photo list){ $ photo list。每个(img={//load img.src=$(img))。图片根据位置滑出时的attr(' data-src ');});} }//初始化图片延迟加载$('。第')页。on ('click ',function(){ new imagelaziloader($(' img。照片’);});这是一个偷懒加载图片的模型。每次点击分页,都会清除上一页的数据,更新到当前页的DOM,重新初始化一个惰性加载引擎。它监视滚动事件并处理传入图片列表的DOM。每次分页时,都会创建一个新的页面,这里会发生内存泄漏,这主要是由以下3行代码引起的:
$(窗口)。on('scroll ',()={ this . show image($ photo list);});因为这里的事件绑定形成了一个闭包,所以这两个变量,this/$photoList,从来没有被释放过。这指的是ImageLazyLoader的一个实例,而$photoList指的是DOM节点。当上一页的数据被清除后,相关的DOM节点已经从DOM树中分离出来,但是仍然有一个$photoList指向它们,这就导致这些DOM节点不能被垃圾收集,一直保留在内存中。因为这个变量也被闭包困住了,没有被释放,所以还有一个ImageLazyLoader的实例存在内存泄漏。
这个问题的解决方案很简单,就是在销毁实例时关闭绑定事件,如下面的代码所示:
class ImageLazyLoader { constructor($ photoList){ this . scrollsshow=()={ this . show image($ photoList);};$(窗口)。on('scroll ',this . scrollsshow);}//添加事件解除绑定清除(){$(窗口)。关闭('滚动',这个。滚动显示);} show image($ photo list){ $ photo list。每个(img={//load img.src=$(img))。图片根据位置滑出时的attr(' data-src ');});//确定是否所有图片都已显示,如果(this . all showed){ this . clear();} } }//初始化图片惰性加载器=点击分页时为空;$('.第')页。on('click ',function(){ LazyLoader(LazyLoader . clear());LazyLoader=new imageLazyLoader($(' img . photo '));});在每次实例化ImageLazyLoader之前清除上一个实例,并在清除中解除绑定。由于JS有构造函数但没有解构函数,所以需要自己写一个clear,在外面手动调整clear。同时,在事件执行期间,事件会在正确的时间自动解除绑定。判断如果显示了所有图片,则不需要监控滚动事件。这样可以解决内存泄漏的问题,触发自动垃圾回收。
为什么要解除事件绑定,这样就不会有闭包引用了?因为JS引擎检测到闭包没用,破坏了闭包承保,闭包引用的外部变量自然会留空。
好了,这是基础知识。现在用Chrome devtools的内存检测工具实际操作一遍,方便在页面上发现一些内存泄漏行为。为了避免浏览器中安装的一些插件的影响,使用Chome的隐身模式页面,这将禁止所有插件。
然后打开开发工具,切换到内存选项卡,选择堆快照,如下图所示:
什么是堆快照?转换为堆快照,拍摄当前内存堆的图片。因为动态应用的内存在堆中,而本地变量在由操作系统管理的内存堆栈中,所以内存不会泄漏。所以关注桩的情况就好。
然后做一些添加、删除、改变DOM的操作,比如:
(1)播放一个盒子,然后关闭盒子
(2)单击单页跳转到另一条路线,然后单击返回返回
(3)点击分页触发动态DOM变更
也就是先添加DOM,然后删除这些DOM,看看这些删除的DOM中是否有引用它们的对象。
这里,我是第二种模式的场景,它检测单页应用程序的路由页中是否存在内存泄漏。先打开首页,点击另一页,然后点击返回,再点击垃圾收集按钮:
触发垃圾收集,避免不必要的干扰。
然后再次点按照片按钮:
它将扫描并显示当前页面的内存堆,如下图所示:
然后在上面中间的类别过滤器的搜索框中搜索分离:
它将显示与DOM树分离的所有DOM节点,重点关注距离值不为空的事实,它表示与DOM根节点的距离。上面显示的这些div是什么?让我们把鼠标放上去等待2s,它会显示这个div的DOM信息:
从类名和其他信息可以知道,它是要检查的页面的DOM节点。在以下对象的窗口中依次展开其父节点,您可以看到其最外面的父节点是一个VueComponent实例:
下面的黄色字体native_bind表示事件指向它,黄色表示引用仍然有效。将鼠标放在native_bind上2秒钟:
它会提示你在“家庭作业-web.vue”文件中有一个绑定到窗口的getScale函数。检查此文件是否有绑定:
mounted(){ window . addeventlistener(' resize ',this . getscale);}所以虽然Vue组件删除了DOM,但是有一个引用,导致组件实例没有发布,组件中还有另外一个$el指向DOM,所以DOM也没有发布。
在毁灭前解开
beforeddestroled(){ window . removeeventlistener(' resize ',this . getscale);}因此,基于以上分析,可能存在以下导致内存泄漏的情况:
(1)在窗口/身体等事件中,监控未解除。
(2)与事件总线相关联的事件未解绑
(3)在3)Vuex的$store手表之后,就没有unwatch了。
(4)模块形成的闭包内部变量使用后不设置为空
(5)由第三方库创建,未调用正确的销毁函数
并且可以使用Chrome的内存分析工具进行快速故障排除。本文主要使用内存堆快照的基本功能,读者可以尝试通过做一些操作来分析自己的页面上是否存在内存泄漏,如弹出一个框并将其关闭、拍摄堆快照、搜索分离、按距离排序、将非空节点扩展到其父节点,并找到标记为黄色的单词来表示存在未释放的引用。也就是说,这个方法主要分析仍然有引用的自由DOM节点。因为页面的内存泄漏通常与DOM有关,所以普通的JS变量不会因为垃圾收集而出现问题,除非变量被捕获并用完而没有被闭包清空。
DOM相关的内存泄漏通常是由闭包和事件绑定引起的。在绑定(全局)事件后,当您不需要它时,您需要解除绑定。当然,直接绑定到div的可以直接删除,绑定到它的事件自然会被解开。
版权声明:vue使用中的内存泄漏[推荐]是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。