详解Vue依赖收集引发的问题
问题背景
在我们的项目中有一个可视化配置的模块,是通过go.js生成canvas来实现的。但是,我们发现这个模块在浏览器中经常会引起该tab页崩溃。开启chrome的任务管理器一看,进入该页面内存和cpu就会暴涨,内存经常会飙到700多m。但是我们的canvas实际的像素只有约500x500,根据一些粗略的计算,大概只占了1m的内存,这个计算过程可参考100*100的 canvas 占多少内存。那么我们这700m内存是哪里来的呢?
定位问题
我们可以使用chrome开发者工具来分析我们的调用栈。这边我是先通过performance来帮助我们定位问题,它会帮我们生成一段过程中一些数据的变化,包括js堆内存、dom节点数量、动画帧等数据,如图:
这是切换至一个canvas画布较大的一个模块的performance分析表现,可以看到占用了472m的内存。下面折线图蓝色部分是js堆内存的变化,而main下面黄色与紫色的矩形框就是我们的调用栈,上下两部分是按照时间一一对应的。可以看到,蓝色的折线呈高低起伏的态势,gc回收之后低点基本和高点持平,因此可以断定几乎不存在内存泄漏的问题。然后我们可以放大去看一看,内存升高的时候,js做了些什么事情,找一找规律。
我们随机找一段内存增长的区域,可以看到在内存增长的过程中,最为频繁调用的就是observer相关的代码。但是就这么看,我们不能够明白observer是在干什么。此时我们可以借用memory选项中的allocation sampling按照javascript function来查看内存分配,我们同样录制以上的一段操作。
此时我们能够清楚的看到,的确是这个observer在作怪。同时,我们可以看到这是vue的代码,点击右边的文件查看source code,就可以清楚的明白这就是vue在执行依赖收集的操作,此时会给属性添加watcher。那我们这里为什么会有如此多的属性被添加了watcher呢?看了一下代码,原来是我把go.js的一个实例挂到了vue的data选项中,放到data中的属性会被vue执行依赖收集的相关操作,而这个实例拥有非常多的嵌套属性,全部都会被添加watcher。其实,我们只是想单纯的存储一下这个实例,供我们后续调用其相关的方法,添加watcher对我们来说完全没有意义,那我们如何避免这样的问题呢?
解决问题
上网搜索了相关的解决方案,大概有如下几种:
- 在data中定义的属性前面加上$,即通知vue该属性不需要被依赖收集,例如:javascript data() { return { $godiagram: null } }但是这样声明,在template中引用时会报找不到$godiagram属性的错误,具体的原因我还没深究,有空可以研究一下。
- 不在data中声明,直接在赋值的时候声明this.godiagram = diagram。这同样会遇到第一种方案的问题,模板中会提示找不到godiagram属性。
- 不在data中声明,而是利用$options来存储godiagram,例如:
export default { godiagram: null, mounted() { this.$options.godiagram = xxx } }
这应该是比较好的一个方法,vue官方中也说明了$options用来包含自定义属性,例如我们平时引入的常量或是枚举类型,我们也不希望它们被添加无意义的watcher,因此可以通过这种方式来定义,在template中引用时只需要{{$options.xxx}}即可。这种方式唯一的缺点就是不能像data那样一眼望去就能清楚地知道你定义了什么属性。
项目中我采用了第一种方式,经过修改后内存占用量减少到原来的1/5到1/6,可以说效果非常好,再也不会出现浏览器崩溃的情况了。
总结
通过这样的一个问题,我们主要能够学习到两点:
- 如何通过chrome的开发者工具,去快速地定位代码中存在的内存问题
- 不要盲目的将属性都挂载到data选项中,一些常量我们可以采取上面提到的几种方式来定义,以此来作为一种优化手段
以上所述是小编给大家介绍的vue依赖收集引发的问题详解整合,希望对大家有所帮助
上一篇: C#之IP地址和整数互转的小例子