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实战_读书笔记2 选择器
Jan 22 Javascript
JavaScript中继承的一些示例方法与属性参考
Aug 07 Javascript
深入理解JavaScript系列(15) 函数(Functions)
Apr 12 Javascript
Jquery选择子控件&quot;大于号&quot;和&quot; &quot;区别介绍及使用示例
Jun 25 Javascript
关于JS数组追加数组采用push.apply的问题
Jun 09 Javascript
JavaScript中检查对象property的存在性方法介绍
Dec 30 Javascript
JavaScript String 对象常用方法总结
Apr 28 Javascript
微信小程序复选框实现多选一功能过程解析
Feb 14 Javascript
JQuery事件冒泡和默认行为代码实例
May 13 jQuery
基于canvasJS在PHP中制作动态图表
May 30 Javascript
Vue记住滚动条和实现下拉加载的完美方法
Jul 31 Javascript
基于脚手架创建Vue项目实现步骤详解
Aug 03 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
获得Google PR值的PHP代码
2007/01/28 PHP
php中json_decode()和json_encode()的使用方法
2012/06/04 PHP
PHP中的类型约束介绍
2015/05/11 PHP
PHP  实现等比压缩图片尺寸和大小实例代码
2016/10/08 PHP
PHP实现断点续传乱序合并文件的方法
2018/09/06 PHP
php新建文件的方法实例
2019/09/26 PHP
关于img的href和src取变量及赋值的方法
2014/04/28 Javascript
BootStrap智能表单实战系列(八)表单配置json详解
2016/06/13 Javascript
js原生代码实现轮播图的实例讲解
2017/07/28 Javascript
Javascript异步执行不按顺序解决方案
2020/04/30 Javascript
Vue CLI4 Vue.config.js标准配置(最全注释)
2020/06/05 Javascript
[05:43]VG.R战队教练Mikasa专访:为目标从未停止战斗
2016/08/02 DOTA
python用来获得图片exif信息的库实例分析
2015/03/16 Python
详解Python中的type()方法的使用
2015/05/21 Python
Python实现列表转换成字典数据结构的方法
2016/03/11 Python
使用python遍历指定城市的一周气温
2017/03/31 Python
Python 文件操作的详解及实例
2017/09/18 Python
Python在不同目录下导入模块的实现方法
2017/10/27 Python
对python中的logger模块全面讲解
2018/04/28 Python
Python 实现引用其他.py文件中的类和类的方法
2018/04/29 Python
Python环境Pillow( PIL )图像处理工具使用解析
2019/09/12 Python
Python解析json代码实例解析
2019/11/25 Python
python openCV自制绘画板
2020/10/27 Python
python tkinter的消息框模块(messagebox,simpledialog)
2020/11/07 Python
美国最好的葡萄酒网上商店:Wine Library
2019/11/02 全球购物
华为消费者德国官方网站:HUAWEI德国
2020/11/03 全球购物
数据库基础的一些面试题
2012/02/25 面试题
药品质量检测应届生求职信
2013/11/14 职场文书
高中毕业生自我鉴定例文
2013/12/29 职场文书
法定代表人授权委托书
2014/04/04 职场文书
2015年三八妇女节活动总结
2015/02/06 职场文书
导游词之河姆渡遗址博物馆
2019/10/10 职场文书
python使用opencv对图像添加噪声(高斯/椒盐/泊松/斑点)
2022/04/06 Python
SQL Server表分区降低运维和维护成本
2022/04/08 SQL Server
mysql5.5中文乱码问题解决的有用方法
2022/05/30 MySQL
Python自动化实战之接口请求的实现
2022/05/30 Python