Vue优化:常见会导致内存泄漏问题及优化详解


Posted in Javascript onAugust 04, 2020

如果你在用 Vue 开发应用,那么就要当心内存泄漏的问题。这个问题在单页应用 (SPA) 中尤为重要,因为在 SPA 的设计中,用户使用它时是不需要刷新浏览器的,所以 JavaScript 应用需要自行清理组件来确保垃圾回收以预期的方式生效。因此在vue开发过程中,你需要时刻警惕内存泄漏的问题,这些内存泄漏往往会发生在使用 Vue 之外的其它进行 DOM 操作的三方库时,请确保测试应用的内存泄漏问题并在适当的时机做必要的组件清理。

下面是我开发过程中遇到,并查资料总结的内存泄漏问题,会持续更新中

一、vue自定义指令给元素绑定事件,却没有解绑事件

这个问题见上篇博客,vue自定义指令导致的内存泄漏问题解决

二、v-if指令产生的内存泄露

v-if也是一个容易产生内存泄漏的地方。因为:

1、v-if绑定到false的值,但是实际上dom元素在隐藏的时候没有被真实的释放掉

2、就是非常常见的比如我们通过v-if删除了父级元素,但是并没有移除父级元素里的dom片段。通常产生于使用第三方库的时候,比如下面的示例中,我们加载了一个带有非常多选项的选择框,然后我们用到了一个显示/隐藏按钮,通过一个 v-if 指令从虚拟 DOM 中添加或移除它。这个示例的问题在于这个 v-if 指令会从 DOM 中移除父级元素,但是我们并没有清除由 Choices.js 新添加的 DOM 片段,从而导致了内存泄漏。

<link rel="stylesheet prefetch" href="https://joshuajohnson.co.uk/Choices/assets/styles/css/choices.min.css?version=3.0.3" rel="external nofollow" >
<script src="https://joshuajohnson.co.uk/Choices/assets/scripts/dist/choices.min.js?version=3.0.3"></script>

<div id="app">
 <button v-if="showChoices" @click="hide">Hide</button>
 <button v-if="!showChoices" @click="show" >Show</button>
 <div v-if="showChoices">
 <select id="choices-single-default"></select>
 </div>
</div>
new Vue({
 el: "#app",
 data: function () {
 return {
  showChoices: true
 }
 },
 mounted: function () {
 this.initializeChoices()
 },
 methods: {
 initializeChoices: function () {
  let list = []
  // 我们来为选择框载入很多选项
  // 这样的话它会占用大量的内存
  for (let i = 0; i < 1000; i++) {
  list.push({
   label: "Item " + i,
   value: i
  })
  }
  new Choices("#choices-single-default", {
  searchEnabled: true,
  removeItemButton: true,
  choices: list
  })
 },
 show: function () {
  this.showChoices = true
  this.$nextTick(() => {
  this.initializeChoices()
  })
 },
 hide: function () {
  this.showChoices = false
 }
 }
})

解决实例:在上述的示例中,我们可以用 hide() 方法在将选择框从 DOM 中移除之前做一些清理工作,来解决内存泄露问题。为了做到这一点,我们会在 Vue 实例的数据对象中保留一个属性,并会使用 Choices API 中的 destroy() 方法将其清除。

new Vue({
 el: "#app",
 data: function () {
 return {
  showChoices: true,
  choicesSelect: null
 }
 },
 mounted: function () {
 this.initializeChoices()
 },
 methods: {
 initializeChoices: function () {
  let list = []
  for (let i = 0; i < 1000; i++) {
  list.push({
   label: "Item " + i,
   value: i
  })
  }
  // 在我们的 Vue 实例的数据对象中设置一个 `choicesSelect` 的引用
  this.choicesSelect = new Choices("#choices-single-default", {
  searchEnabled: true,
  removeItemButton: true,
  choices: list
  })
 },
 show: function () {
  this.showChoices = true
  this.$nextTick(() => {
  this.initializeChoices()
  })
 },
 hide: function () {
  // 现在我们可以让 Choices 使用这个引用
  // 在从 DOM 中移除这些元素之前进行清理工作
  this.choicesSelect.destroy()
  this.showChoices = false
 }
 }
})

三、vue-router跳转到别的组件导致的内容泄漏

在上述示例中,我们使用了一个 v-if 指令产生内存泄漏,但是一个更常见的实际的场景是使用 Vue Router 在一个单页应用中路由到不同的组件。

就像这个 v-if 指令一样,当一个用户在你的应用中导航时,Vue Router 从虚拟 DOM 中移除了元素,并替换为了新的元素。但是其子元素dom片段也并没有销毁。

Vue 的 beforeDestroy() 生命周期钩子是一个解决基于 Vue Router 的应用中的这类问题的好方法。我们可以将清理工作放入 beforeDestroy() 钩子,像这样:

beforeDestroy: function () {
 this.choicesSelect.destroy()
}

所以最正确的解决方案就是:首先,v-if置为false前先删除创建的dom片段;其次,路由跳出吃,在beforeDestroy钩子函数里面判断choicesSelect是否销毁,没销毁则销毁。

还有一个替代方案:

我们已经讨论了移除元素时的内存管理,但是如果你打算在内存中保留状态和元素该怎么做呢?这种情况下,你可以使用内建的 keep-alive 组件。

当你用 keep-alive 包裹一个组件后,它的状态就会保留,因此就留在了内存里。

<button @click="show = false">Hide</button>
<keep-alive>
 // <my-component> 即便被删除仍会刻意保留在内存里
 <my-component v-if="show"></my-component>
</keep-alive>

这个技巧可以用来提升用户体验。例如,设想一个用户在一个文本框中输入了评论,之后决定导航离开。如果这个用户之后导航回来,那些评论应该还保留着。

一旦你使用了 keep-alive,那么你就可以访问另外两个生命周期钩子:activated和 deactivated。如果你想要在一个 keep-alive 组件被移除的时候进行清理或改变数据,可以使用 deactivated 钩子。

deactivated: function () {
 // 移除任何你不想保留的数据,或者销毁可能产生内存泄漏的地方
}

以上这篇Vue优化:常见会导致内存泄漏问题及优化详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript delete操作符应用实例
Jan 13 Javascript
Jquery实现点击切换图片并隐藏显示内容(2种方法实现)
Apr 11 Javascript
由点击页面其它地方隐藏div所想到的jQuery的delegate
Aug 29 Javascript
js控制当再次点击按钮时的间隔时间
Jun 03 Javascript
JavaScript操作Cookie详解
Feb 28 Javascript
JavaScript实现广告的关闭与显示效果实例
Jul 02 Javascript
jQuery fancybox在ie浏览器下无法显示关闭按钮的解决办法
Feb 19 Javascript
jQuery获取字符串中出现最多的数
Feb 22 Javascript
简介BootStrap model弹出框的使用
Apr 27 Javascript
微信小程序 网络API 上传、下载详解
Nov 09 Javascript
详解react-refetch的使用小例子
Feb 15 Javascript
jQuery实现判断滚动条滚动到document底部的方法分析
Aug 27 jQuery
Jquery cookie插件实现原理代码解析
Aug 04 #jQuery
解决vue自定义指令导致的内存泄漏问题
Aug 04 #Javascript
vue中的v-model原理,与组件自定义v-model详解
Aug 04 #Javascript
详解JS深拷贝与浅拷贝
Aug 04 #Javascript
vue addRoutes路由动态加载操作
Aug 04 #Javascript
vue+element使用动态加载路由方式实现三级菜单页面显示的操作
Aug 04 #Javascript
Vue登录拦截 登录后继续跳转指定页面的操作
Aug 04 #Javascript
You might like
PHP 中的面向对象编程:通向大型 PHP 工程的办法
2006/12/03 PHP
一些使用频率比较高的php函数
2008/10/03 PHP
PHP取整函数:ceil,floor,round,intval的区别详细解析
2013/08/31 PHP
Yii2使用小技巧之通过 Composer 添加 FontAwesome 字体资源
2014/06/22 PHP
PHP实现类似于C语言的文件读取及解析功能
2017/09/01 PHP
限制textbox或textarea输入字符长度的JS代码
2013/10/16 Javascript
js获取本机操作系统类型的两种方法
2015/12/19 Javascript
原生js实现查询天气小应用
2016/12/09 Javascript
详解照片瀑布流效果(js,jquery分别实现与知识点总结)
2017/01/01 Javascript
js 实现省市区三级联动菜单效果
2017/02/20 Javascript
MUI 上拉刷新/下拉加载功能实例代码
2017/04/13 Javascript
Vue.js列表渲染绑定jQuery插件的正确姿势
2017/06/29 jQuery
react组件从搭建脚手架到在npm发布的步骤实现
2019/01/09 Javascript
vue基于两个计算属性实现选中和全选功能示例
2019/02/08 Javascript
js字符串处理之绝妙的代码
2019/04/05 Javascript
一篇文章介绍redux、react-redux、redux-saga总结
2019/05/23 Javascript
thinkjs微信中控之微信鉴权登陆的实现代码
2019/08/08 Javascript
详解vue-router 动态路由下子页面多页共活的解决方案
2019/12/22 Javascript
JavaScript实现浏览器网页自动滚动并点击的示例代码
2020/12/05 Javascript
Python两个整数相除得到浮点数值的方法
2015/03/18 Python
Python CSV模块使用实例
2015/04/09 Python
使用Node.js和Socket.IO扩展Django的实时处理功能
2015/04/20 Python
深度定制Python的Flask框架开发环境的一些技巧总结
2016/07/12 Python
python中range()与xrange()用法分析
2016/09/21 Python
Python安装pycurl失败的解决方法
2018/10/15 Python
python的schedule定时任务模块二次封装方法
2019/02/19 Python
详解pandas中利用DataFrame对象的.loc[]、.iloc[]方法抽取数据
2020/12/13 Python
一款CSS3实现多功能下拉菜单(带分享按)的教程
2014/11/05 HTML / CSS
Ellos丹麦:时尚和服装在线
2016/09/19 全球购物
美国值得信赖的婚恋交友网站:eHarmony
2018/10/04 全球购物
大学生活动总结怎么写
2014/04/29 职场文书
现场活动策划方案
2014/08/22 职场文书
公司股东合作协议书
2014/09/14 职场文书
小学感恩节活动策划方案
2014/10/06 职场文书
2014年医德医风工作总结
2014/11/13 职场文书
五一劳动节活动总结
2015/02/09 职场文书