vue单页应用的内存泄露定位和修复问题小结


Posted in Javascript onAugust 02, 2019

在前端项目(PC端)中,内存泄露的定位往往比修复更加困难,即使google浏览器有提供Memory工具,但是面对成千上万的元素和错综复杂的引用关系,开发则依然很难快速定位到问题代码块。

一、什么是内存泄漏?

系统进程不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。当内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。Chrome限制了浏览器所能使用的内存极限(64位为1.4GB,32位为1.0GB),这也就意味着浏览器将无法直接操作一些大内存对象。

V8引擎在执行垃圾回收时会阻塞 JavaScript应用逻辑,直到垃圾回收结束再重新执行JavaScript应用逻辑,这种行为被称为“全停顿”(stop-the-world)。 若V8的堆内存为1.5GB,V8做一次小的垃圾回收需要50ms以上,造成假死现象。

二、JS内存管理和垃圾回收机制GC

高级语言基本都有垃圾回收机制(garbage collection)自动管理内存,降低程序员的负担,以达到解决内存泄漏的目的,但是不允许人为手动触发,无法对内存管理进行任何干预。

老版本的浏览器使用引用计数法(Reference Counting)来管理内存,即每次引用加一,被释放时减一,当这个值的引用次数变成 0 时,就可以将其内存空间回收,缺点是循环引用时无法回收。

现代浏览器基本采用标记清除法(Mark-and-Sweep)来管理内存,即浏览器周期性地从某个根元素(譬如 window 对象)开始找引用变量,及这些变量引用的变量,这样一直找下去。能找到的变量为可获得变量,不能找到的将被内存回收。

vue单页应用的内存泄露定位和修复问题小结

缺点是清除后内存会产生很多细化的分块,所以又衍生了标记-整理法,不细讲。

vue单页应用的内存泄露定位和修复问题小结

三、VUE中容易出现内存泄露的几种情况

内存泄露是一个累积的过程,只有页面生命周期略长的时候才暴露出问题,频繁交互能够加快累积的过程,偏展示的页面很难把这样的问题暴露出来(所谓刷新一下又能满血复活)。所以很多时候我们都是被动式的等待问题暴露然后进行排查的,主动式的分析通常比较难。vue页面大多是单页应用,高交互且停留时间久,处理不好很容易出现内存泄漏。本文章 主要针对游离的dom对象进行排查 ,普通的JS变量排查有时间再补充。

1.全局变量造成的内存泄露

<template>
 <div id="home">
  这里是首页
 </div>
</template>

<script>
export default {
 mounted () {
  window.test = { // 此处在全局window对象中引用了本页面的dom对象
   name: 'home',
   node: document.getElementById('home')
  }
 }
}
</script>

按下Heap snapshots键,搜索Detached,发现没有脱离文档树的dom元素,属于正常现象

vue单页应用的内存泄露定位和修复问题小结

改变路由跳转到other页面,按下Heap snapshots键,搜索Detached,发现有两处dom元素游离于当前页面之外,很明显是window对象引用了home页面中的div,即使此时home页面已经销毁,home中的dom元素却还驻留在内存中无法释放。

vue单页应用的内存泄露定位和修复问题小结

解决方案就是在页面卸载的时候顺便处理掉该引用。

<template>
 <div id="home">
  这里是首页
 </div>
</template>

<script>
export default {
 mounted () {
  window.test = { // 此处在全局window对象中引用了本页面的dom对象
   name: 'home',
   node: document.getElementById('home')
  }
 },
 destroyed () {
  window.test = null // 页面卸载的时候解除引用
 }
}
</script>

2.除了直接引用,window的原生方法也会起到引用dom元素使其无法释放的效果。

<template>
 <div id="home">这里是首页</div>
</template>

<script>
export default {
 mounted () {
  window.addEventListener('resize', this.func) // window对象引用了home页面的方法
 },
 methods: {
  func () {
   console.log('这是home页面的函数')
  }
 }
}
</script>

vue单页应用的内存泄露定位和修复问题小结

 解决方法一样,也是在页面销毁的时候,顺便解除引用,释放内存

mounted () {
  window.addEventListener('resize', this.func)
},
beforeDestroy () {
  window.removeEventListener('resize', this.func)
}

3.一些全局的方法使用不当也会造成内存无法释放,在页面卸载的时候也可以考虑解除引用

<template>
 <div id="home">这里是首页</div>
</template>

<script>
export default {
 mounted () {
  this.$EventBus.$on('homeTask', res => this.func(res))
 },
 methods: {
  func (res) {
   console.log(res)
  }
 }
}
</script>

vue单页应用的内存泄露定位和修复问题小结

mounted () {
 this.$EventBus.$on('homeTask', res => this.func(res))
},
destroyed () {
 this.$EventBus.$off()
}

造成游离dom节点的原因还有很多,不止这三种,总结起来:

1.window对象、事件总线、全局vuex上绑定了已销毁页面上的节点,到时节点不随页面一起销毁

2.使用第三方库创建实例,第三方库一般会提供销毁函数,页面跳转时没有调用正确的销毁函数

3.有同学会说在页面中使用闭包也会造成内存泄露,在vue框架里有管理内存的机制,只要按照它的正确编写方法,理论上是不会造成内存泄露的

总结

以上所述是小编给大家介绍的vue单页应用的内存泄露定位和修复问题小结,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
保证JavaScript和Asp、Php等后端程序间传值编码统一
Apr 17 Javascript
扩展JS Date对象时间格式化功能的小例子
Dec 02 Javascript
jquery重复提交请求的原因浅析
May 23 Javascript
jQuery实现tab选项卡效果的方法
Jul 08 Javascript
逻辑表达式中与或非的用法详解
Jun 06 Javascript
jQuery和hwSlider实现内容响应式可触控滑动切换效果附源码下载(二)
Jun 22 Javascript
详解Angular.js中$http拦截器的介绍及使用
Jul 04 Javascript
解决koa2 ctx.render is not a function报错问题
Aug 07 Javascript
jQuery+Datatables实现表格批量删除功能【推荐】
Oct 24 jQuery
详解JQuery基础动画操作
Apr 12 jQuery
重学JS之显示强制类型转换详解
Jun 30 Javascript
DWR内存兼容及无法调用问题解决方案
Oct 16 Javascript
vue回到顶部监听滚动事件详解
Aug 02 #Javascript
微信小程序npm引入vant-weapp的踩坑记录
Aug 01 #Javascript
vue3.0中的双向数据绑定方法及优缺点
Aug 01 #Javascript
微信小程序导入Vant报错VM292:1 thirdScriptError的解决方法
Aug 01 #Javascript
微信小程序使用Vant Weapp组件库的方法步骤
Aug 01 #Javascript
微信小程序中button去除默认的边框实例代码
Aug 01 #Javascript
vue-router两种模式区别及使用注意事项详解
Aug 01 #Javascript
You might like
咖啡磨器 如何选购一台适合家用的意式磨豆机
2021/03/05 新手入门
php格式化工具Beautify PHP小小BUG
2008/04/24 PHP
PHP 缓存实现代码及详细注释
2010/05/16 PHP
基于MySQL分区性能的详细介绍
2013/05/02 PHP
thinkPHP通用控制器实现方法示例
2017/11/23 PHP
php使用redis的有序集合zset实现延迟队列应用示例
2020/02/20 PHP
phpStorm2020 注册码
2020/09/17 PHP
JQuery实现的在新窗口打开链接的方法小结
2010/04/22 Javascript
使用JavaScript 实现对象 匀速/变速运动的方法
2013/05/08 Javascript
Jquery+Ajax+PHP+MySQL实现分类列表管理(上)
2015/10/28 Javascript
JavaScript中获取时间的函数集
2016/08/16 Javascript
EasyUI 结合JS导出Excel文件的实现方法
2016/11/10 Javascript
NodeJS实现客户端js加密
2017/01/09 NodeJs
js实现贪吃蛇小游戏(容易理解)
2017/01/22 Javascript
vue resource post请求时遇到的坑
2017/10/19 Javascript
node.js用fs.rename强制重命名或移动文件夹的方法
2017/12/27 Javascript
VUE2.0中Jsonp的使用方法
2018/05/22 Javascript
Vue在页面数据渲染完成之后的调用方法
2018/09/11 Javascript
[02:31]DOTA2帕克 英雄基础教程
2013/11/26 DOTA
python连接mysql并提交mysql事务示例
2014/03/05 Python
python使用三角迭代计算圆周率PI的方法
2015/03/20 Python
python爬虫面试宝典(常见问题)
2018/03/02 Python
详解Appium+Python之生成html测试报告
2019/01/04 Python
python读写配置文件操作示例
2019/07/03 Python
快速解决vue.js 模板和jinja 模板冲突的问题
2019/07/26 Python
Python3.7.0 Shell添加清屏快捷键的实现示例
2020/03/23 Python
python实现三种随机请求头方式
2021/01/05 Python
python爬虫scrapy基于CrawlSpider类的全站数据爬取示例解析
2021/02/20 Python
三星新西兰官网:Samsung新西兰
2019/03/05 全球购物
爱鸟护鸟的宣传语
2015/07/13 职场文书
婚宴来宾致辞
2015/07/28 职场文书
父亲节感言
2015/08/03 职场文书
《云雀的心愿》教学反思
2016/02/23 职场文书
CSS3 菱形拼图实现只旋转div 背景图片不旋转功能
2021/03/30 HTML / CSS
Nginx实现高可用集群构建(Keepalived+Haproxy+Nginx)
2021/05/27 Servers
德劲DE1107指针试高灵敏度全波段收音机机评
2022/04/05 无线电