Vue中使用的EventBus有生命周期


Posted in Javascript onJuly 12, 2018

最近遇到了vue项目中的性能问题,整个项目不断的进行操作五分钟左右,页面已经很卡,查看页面占用了1.5G内存,经过排查一部分原因,是自己模块使用的eventBus在离开页面未进行off掉。我们进行下验证:

1、不随生命周期销毁

我们在home首页的代码是这样的:

created () {
 let text = Array(1000000).fill('xxx').join(',') //创建一个大的字符串用于验证内存占用
 $eventBus.$on('home-on', (...args) => {
  this.text = text
 })
 },
 mounted () {
 setTimeout(() => {
  $eventBus.$emit('home-on', '这是home $emit参数', 'ee')
 }, 1000)
 },
 beforeDestroy () {
 // 注意这里没有off掉'home-on'的订阅事件
 }
 // eventBus是全局的

(1)在home页时:我们拍个内存快照查看下home页的内存占用:

Vue中使用的EventBus有生命周期

图1

一共17.4M我们创建出的字符串text占用了8M,这在home页没销毁时是正常的

(2)离开home页时:然后我们点击跳转到其他页面离开home页,然后再拍个内存快照:

Vue中使用的EventBus有生命周期

图2

创建的'xxx,xxx...'字符串是home页面挂载在this.text上的,离开了home依然存在着,查看下面的箭头看到是存在了EventBus上,原因很明显,是我们在beforeDestroy的时候没把订阅的事件给销毁掉,而EventBus是全局的,造订阅的on的回调里调用了this.text,因此回调里的this就一直挂在了EventBus里。

2、随着生命周期销毁

created () {
 let text = Array(1000000).fill('xxx').join(',') //创建一个大的字符串用于验证内存占用
 $eventBus.$on('home-on', (...args) => {
  this.text = text
 })
 },
 mounted () {
 setTimeout(() => {
  $eventBus.$emit('home-on', '这是home $emit参数', 'ee')
 }, 1000)
 },
 beforeDestroy () {
 $eventBus.$off('home-on') // 注意这里off掉了'home-on'的订阅事件
 }
 // eventBus是全局的

(1)在home页时:内存快照不用多说自然是图1的内存快照

(2)离开home页时:再来拍个内存快照:

Vue中使用的EventBus有生命周期

发现减到了10M,且通过内存占用看到'string'里已经没有'xxx,xxx...'的内存占用了,这说明我们销毁之后浏览器回收了this.text。

好,以上说这么多只是说明在使用EventBus时要时刻注意订阅事件的销毁。如果有一个还好,如果有5个,6个...也要挨个销毁这就比较麻烦了。话说off销毁这件重复性劳动这些都应该是机器做的事情,我们不应该去关心的。

基于以上我们自然就想到让EventBus随着生命周期销毁就行了嘛。EventBus有生命周期的特性那么就离不开在使用.$on的this的关联,每个Vue组件有自己的_uid作为唯一标识,因此我们基于uid稍微改造下EventBus,让EventBus和_uid关联起来:

class EventBus {
 constructor (vue) {
 if (!this.handles) {
  Object.defineProperty(this, 'handles', {
  value: {},
  enumerable: false
  })
 }
 this.Vue = vue
 this.eventMapUid = {} // _uid和EventName的映射
 }
 setEventMapUid (uid, eventName) {
 if (!this.eventMapUid[uid]) this.eventMapUid[uid] = []
 this.eventMapUid[uid].push(eventName) // 把每个_uid订阅的事件名字push到各自uid所属的数组里
 }
 $on (eventName, callback, vm) { // vm是在组件内部使用时组件当前的this用于取_uid
 if (!this.handles[eventName]) this.handles[eventName] = []
 this.handles[eventName].push(callback)
 if (vm instanceof this.Vue) this.setEventMapUid(vm._uid, eventName)
 }
 $emit () {
 // console.log('EventBus emit eventName===', eventName)
 let args = [...arguments]
 let eventName = args[0]
 let params = args.slice(1)
 if (this.handles[eventName]) {
  let len = this.handles[eventName].length
  for (let i = 0; i < len; i++) {
  this.handles[eventName][i](...params)
  }
 }
 }
 $offVmEvent (uid) {
 let currentEvents = this.eventMapUid[uid] || []
 currentEvents.forEach(event => {
  this.$off(event)
 })
 }
 $off (eventName) {
 delete this.handles[eventName]
 }
}
// 下面写成Vue插件形式,直接引入然后Vue.use($EventBus)进行使用
let $EventBus = {}
$EventBus.install = (Vue, option) => {
 Vue.prototype.$eventBus = new EventBus(Vue)
 Vue.mixin({
 beforeDestroy () {
  this.$eventBus.$offVmEvent(this._uid) // 拦截beforeDestroy钩子自动销毁自身所有订阅的事件
 }
 })
}
export default $EventBus

下面来进行使用

// main.js中
...
import EventBus from './eventBus.js'
Vue.use(EnemtBus)
...

组件中使用:

created () {
 let text = Array(1000000).fill('xxx').join(',')
 this.$eventBus.$on('home-on', (...args) => {
  console.log('home $on====>>>', ...args)
  this.text = text
 }, this) // 注意第三个参数需要传当前组件的this,如果不传则需要手动销毁
 },
 mounted () {
 setTimeout(() => {
  this.$eventBus.$emit('home-on', '这是home $emit参数', 'ee')
 }, 1000)
 },
 beforeDestroy () {
 // 这里就不需要手动的off销毁eventBus订阅的事件了
 }

总结

以上所述是小编给大家介绍的Vue中使用的EventBus有生命周期,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
基于jquery实现图片广告轮换效果代码
Jul 07 Javascript
JS Replace()的高级使用方法介绍
Jun 29 Javascript
javascript如何实现暂停功能
Nov 06 Javascript
JS实时弹出新消息提示框并有提示音响起的实现代码
Apr 20 Javascript
探讨跨域请求资源的几种方式(总结)
Dec 02 Javascript
jQuery插件ContextMenu自定义图标
Mar 15 Javascript
分享十三个最佳JavaScript数据网格库
Apr 07 Javascript
vue.js学习之vue-cli定制脚手架详解
Jul 02 Javascript
js实现canvas保存图片为png格式并下载到本地的方法
Aug 31 Javascript
angular2/ionic2 实现搜索结果中的搜索关键字高亮的示例
Aug 17 Javascript
angular1.x ui-route传参的三种写法小结
Aug 31 Javascript
vue print.js打印支持Echarts图表操作
Nov 13 Javascript
JavaScript中发出HTTP请求最常用的方法
Jul 12 #Javascript
vue实现引入本地json的方法分析
Jul 12 #Javascript
jQuery实现checkbox全选功能完整实例
Jul 12 #jQuery
JS实现将二维数组转为json格式字符串操作示例
Jul 12 #Javascript
vue路由组件按需加载的几种方法小结
Jul 12 #Javascript
js与jQuery实现获取table中的数据并拼成json字符串操作示例
Jul 12 #jQuery
使用json-server简单完成CRUD模拟后台数据的方法
Jul 12 #Javascript
You might like
PHP中用hash实现的数组
2011/07/17 PHP
php获取随机数组列表的方法
2014/11/13 PHP
PHP中PDO连接数据库中各种DNS设置方法小结
2016/05/13 PHP
Laravel如何实现自动加载类
2019/10/14 PHP
JavaScript 判断指定字符串是否为有效数字
2010/05/11 Javascript
Jquery+ajax请求data显示在GridView上(asp.net)
2010/08/27 Javascript
javascript针对DOM的应用实例(一)
2012/04/15 Javascript
javascript简单实现表格行间隔显示颜色并高亮显示
2013/11/29 Javascript
jquery xMarquee实现文字水平无缝滚动效果
2014/04/29 Javascript
PHPMyAdmin导入时提示文件大小超出PHP限制的解决方法
2015/03/30 Javascript
JS简单实现动画弹出层效果
2015/05/05 Javascript
jquery.map()方法的使用详解
2015/07/09 Javascript
ES6记录异步函数的执行时间详解
2016/08/31 Javascript
从零学习node.js之搭建http服务器(二)
2017/02/21 Javascript
从零开始学习Node.js系列教程之设置HTTP头的方法示例
2017/04/13 Javascript
vue不通过路由直接获取url中参数的方法示例
2017/08/24 Javascript
JavaScript实现职责链模式概述
2018/01/25 Javascript
浅谈Angular 的变化检测的方法
2018/03/01 Javascript
浅谈node.js 命令行工具(cli)
2018/05/10 Javascript
JavaScript设计模式之原型模式分析【ES5与ES6】
2018/07/26 Javascript
如何在JavaScript中谨慎使用代码注释
2019/06/21 Javascript
[49:08]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第一场 11.27
2020/12/01 DOTA
Android模拟器无法启动,报错:Cannot set up guest memory ‘android_arm’ Invalid argument的解决方法
2016/07/01 Python
快速排序的算法思想及Python版快速排序的实现示例
2016/07/02 Python
python去除空格和换行符的实现方法(推荐)
2017/01/04 Python
彻底理解Python list切片原理
2017/10/27 Python
Python设置在shell脚本中自动补全功能的方法
2018/06/25 Python
python+selenium实现QQ邮箱自动发送功能
2019/01/23 Python
使用Python实现牛顿法求极值
2020/02/10 Python
详解快速开发基于 HTML5 网络拓扑图应用
2018/01/08 HTML / CSS
意大利拉斐尔时尚购物网:Raffaello Network(支持中文)
2018/11/09 全球购物
2015年感恩父亲节演讲稿
2015/03/19 职场文书
2015夏季作息时间调整通知
2015/04/24 职场文书
详细聊聊浏览器是如何看闭包的
2021/11/11 Javascript
Python数据处理的三个实用技巧分享
2022/04/01 Python
避坑之 JavaScript 中的toFixed()和正则表达式
2022/04/19 Javascript