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之超简单的div显示和隐藏特效demo(分享)
Jul 09 Javascript
JavaScript作用域链示例分享
May 27 Javascript
JSP中使用JavaScript动态插入删除输入框实现代码
Jun 13 Javascript
Jquery实现简单的轮播效果(代码管用)
Mar 14 Javascript
jQuery实现手机自定义弹出输入框
Jun 13 Javascript
js实现控制textarea输入字符串的个数,鼠标按下抬起判断输入字符数
Oct 25 Javascript
angularJS 指令封装回到顶部示例详解
Jan 22 Javascript
js实现数组去重方法及效率?Ρ? target=
Feb 14 Javascript
JS批量替换内容中关键词为超链接
Feb 20 Javascript
详解Node中导入模块require和import的区别
Aug 11 Javascript
基于vue cli 通过命令行传参实现多环境配置
Jul 12 Javascript
javascript设计模式 ? 组合模式原理与应用实例分析
Apr 14 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
第八节 访问方式 [8]
2006/10/09 PHP
php中is_null,empty,isset,unset 的区别详细介绍
2013/04/28 PHP
PHP按行读取、处理较大CSV文件的代码实例
2014/04/09 PHP
PHP翻页跳转功能实现方法
2020/11/30 PHP
学习php设计模式 php实现合成模式(composite)
2015/12/08 PHP
使用Entrust扩展包在laravel 中实现RBAC的功能
2020/03/16 PHP
070823更新的一个[消息提示框]组件 兼容ie7
2007/08/29 Javascript
JavaScript this 深入理解
2009/07/30 Javascript
js关闭浏览器窗口及检查浏览器关闭事件
2013/09/03 Javascript
页面加载完成后再执行JS的jquery写法以及区别说明
2014/02/22 Javascript
JS获得图片alt信息的方法
2015/04/01 Javascript
jquery获取css的color值返回RGB的方法
2015/12/18 Javascript
Bootstrap打造一个左侧折叠菜单的系统模板(二)
2016/05/17 Javascript
浅谈angularjs中响应回车事件
2017/04/24 Javascript
Vue实现typeahead组件功能(非常靠谱)
2017/08/26 Javascript
jQuery实现表单动态添加与删除数据操作示例
2018/07/03 jQuery
详解webpack打包时排除其中一个css、js文件或单独打包一个css、js文件(两种方法)
2018/10/26 Javascript
javascript刷新父页面方法汇总详解
2019/10/10 Javascript
[01:38]完美世界高校联赛决赛花絮
2018/12/02 DOTA
Python读取mp3中ID3信息的方法
2015/03/05 Python
详解Python发送邮件实例
2016/01/10 Python
Python使用Beautiful Soup包编写爬虫时的一些关键点
2016/01/20 Python
解决pandas 作图无法显示中文的问题
2018/05/24 Python
python set内置函数的具体使用
2019/07/02 Python
详解Python遍历列表时删除元素的正确做法
2021/01/07 Python
韩国知名的家庭购物网站:CJmall
2016/08/01 全球购物
德国苹果商店:MacTrade
2020/05/18 全球购物
新郎新娘婚礼答谢词
2014/01/11 职场文书
市场部规章制度
2014/01/24 职场文书
设计师个人求职信范文
2014/02/02 职场文书
化学专业自荐信
2014/05/28 职场文书
2014年乡镇党建工作总结
2014/11/11 职场文书
2015年骨干教师工作总结
2015/05/26 职场文书
小米11和iphone12哪个值得买?小米11对比iphone12评测
2021/04/21 数码科技
MySQL 全文索引使用指南
2021/05/25 MySQL
分析Python感知线程状态的解决方案之Event与信号量
2021/06/16 Python