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 相关文章推荐
js cookies 常见网页木马挂马代码 24小时只加载一次
Apr 13 Javascript
用jquery与css打造个性化的单选框和复选框
Oct 20 Javascript
Javascript封装DOMContentLoaded事件实例
Jun 12 Javascript
js实现鼠标悬停图片上时滚动文字说明的方法
Feb 17 Javascript
javaScript实现滚动新闻的方法
Jul 30 Javascript
js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)
Nov 09 Javascript
使用bootstrap-paginator.js 分页来进行ajax 异步分页请求示例
Mar 09 Javascript
JS全角与半角转化实例(分享)
Jul 04 Javascript
JavaScript 判断对象中是否有某属性的常用方法
Jun 14 Javascript
JavaScript实现的前端AES加密解密功能【基于CryptoJS】
Aug 28 Javascript
Vue.js实现可排序的表格组件功能示例
Feb 19 Javascript
使用webpack搭建vue环境的教程详解
Dec 31 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
source.php查看源文件
2006/12/09 PHP
Opcache导致php-fpm崩溃nginx返回502
2015/03/02 PHP
Laravel框架实现抢红包功能示例
2019/10/31 PHP
PHP dirname功能及原理实例解析
2020/10/28 PHP
用 JavaScript 迁移目录
2006/12/18 Javascript
Extjs4 关于Store的一些操作(加载/回调/添加)
2013/04/18 Javascript
浏览器页面区域大小的js获取方法
2013/09/21 Javascript
Extjs grid添加一个图片状态或者按钮的方法
2014/04/03 Javascript
jQuery实现复选框全选/取消全选/反选及获得选择的值
2014/06/12 Javascript
jquery表单验证插件(jquery.validate.js)的3种使用方式
2015/03/28 Javascript
jquery控制页面部分刷新的方法
2015/06/24 Javascript
Css3制作变形与动画效果
2015/07/24 Javascript
使用HTML+CSS+JS制作简单的网页菜单界面
2015/07/27 Javascript
JS右下角广告窗口代码(可收缩、展开及关闭)
2015/09/04 Javascript
第七篇Bootstrap表单布局实例代码详解(三种表单布局)
2016/06/21 Javascript
AngularJS包括详解及示例代码
2016/08/17 Javascript
通过bootstrap全面学习less
2016/11/09 Javascript
vue小白入门教程
2018/04/02 Javascript
.vue文件 加scoped 样式不起作用的解决方法
2018/05/28 Javascript
原生javascript运动函数的封装示例【匀速、抛物线、多属性的运动等】
2020/02/23 Javascript
js实现简单进度条效果
2020/03/25 Javascript
js观察者模式的弹幕案例
2020/11/23 Javascript
vue3弹出层V3Popup实例详解
2021/01/04 Vue.js
使用matplotlib中scatter方法画散点图
2019/03/19 Python
使用python的turtle绘画滑稽脸实例
2019/11/21 Python
使用pyshp包进行shapefile文件修改的例子
2019/12/06 Python
Pytorch 的损失函数Loss function使用详解
2020/01/02 Python
Spartoo葡萄牙鞋类网站:线上销售鞋履与时尚配饰
2017/01/11 全球购物
我的网上商城创业计划书
2013/12/26 职场文书
办公室人员先进事迹
2014/01/27 职场文书
项目投资合作意向书
2014/07/29 职场文书
心得体会的写法
2014/09/05 职场文书
高等学院职业生涯规划书范文
2014/09/16 职场文书
2015年度优秀员工推荐信
2015/03/23 职场文书
2016大一新生军训心得体会
2016/01/11 职场文书
网络安全倡议书(3篇)
2019/09/18 职场文书