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 相关文章推荐
如何实现iframe(嵌入式帧)的自适应高度
Jul 26 Javascript
对JavaScript的eval()中使用函数的进一步讨论
Jul 26 Javascript
jQuery Ajax使用 全解析
Dec 15 Javascript
js调试工具console.log()方法查看js代码的执行情况
Aug 08 Javascript
JavaScript Math 对象常用方法总结
Apr 28 Javascript
javascript 秒表计时器实现代码
Mar 09 Javascript
jQuery遮罩层实例讲解
May 11 jQuery
JavaScript生成图形验证码
Aug 24 Javascript
Angular中使用MathJax遇到的一些问题
Dec 15 Javascript
p5.js入门教程之小球动画示例代码
Mar 15 Javascript
vue根据进入的路由进行原路返回的方法
Sep 26 Javascript
bootstrap-table实现表头固定以及列固定的方法示例
Mar 07 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之第十天
2006/10/09 PHP
PHP中获取内网用户MAC地址(WINDOWS/linux)的实现代码
2011/08/11 PHP
php数组函数序列之array_push() 数组尾部添加一个或多个元素(入栈),返回新长度。
2011/11/07 PHP
逆序二维数组插入一元素的php代码
2012/06/08 PHP
php-fpm重启导致的程序执行中断问题详解
2019/04/29 PHP
初窥JQuery(二) 事件机制(1)
2010/11/25 Javascript
js中scrollHeight,scrollWidth,scrollLeft,scrolltop等差别介绍
2012/05/16 Javascript
javascript包装对象实例分析
2015/03/27 Javascript
jquery使整个div区域可以点击的方法
2015/06/24 Javascript
JavaScript知识点总结之如何提高性能
2016/01/15 Javascript
深入理解JavaScript中的浮点数
2016/05/18 Javascript
微信小程序 label 组件详解及简单实例
2017/01/10 Javascript
jQuery实现简单弹窗遮罩效果
2017/02/27 Javascript
JS实现的自动打字效果示例
2017/03/10 Javascript
js图片放大镜实例讲解(必看篇)
2017/07/17 Javascript
Koa代理Http请求的示例代码
2018/10/10 Javascript
微信小程序模板template简单用法示例
2018/12/04 Javascript
用Electron写个带界面的nodejs爬虫的实现方法
2019/01/29 NodeJs
Vue组件的使用及个人理解与介绍
2019/02/09 Javascript
layui lay-verify form表单自定义验证规则详解
2019/09/18 Javascript
[52:14]VG vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
在Django的视图中使用数据库查询的方法
2015/07/16 Python
python自动查询12306余票并发送邮箱提醒脚本
2018/05/21 Python
Python计算机视觉里的IOU计算实例
2020/01/17 Python
解决pycharm不能自动补全第三方库的函数和属性问题
2020/03/12 Python
更新升级python和pip版本后不生效的问题解决
2020/04/17 Python
Keras设置以及获取权重的实现
2020/06/19 Python
html5指南-4.使用Geolocation实现定位功能
2013/01/07 HTML / CSS
SK-II神仙水美国官网:SK-II美国
2020/02/25 全球购物
Silk Therapeutics官网:清洁、抗衰老护肤品
2020/08/12 全球购物
接口中的方法可以是abstract的吗
2015/07/23 面试题
会计系毕业求职信
2014/08/07 职场文书
2014年党员整改措施范文
2014/09/21 职场文书
导游词之永泰公主墓
2019/12/04 职场文书
总结Java对象被序列化的两种方法
2021/06/30 Java/Android
JavaScript的function函数详细介绍
2021/11/20 Javascript