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 相关文章推荐
破除网页鼠标右键被禁用的绝招大全
Dec 27 Javascript
Js控制弹窗实现在任意分辨率下居中显示
Aug 01 Javascript
JavaScript如何从listbox里同时删除多个项目
Oct 12 Javascript
Javascript浅谈之引用类型
Dec 18 Javascript
JavaScript中对循环语句的优化技巧深入探讨
Jun 06 Javascript
Bootstrap栅格系统学习笔记
Nov 25 Javascript
详解js数组的完全随机排列算法
Dec 16 Javascript
详解axios在node.js中的post使用
Apr 27 Javascript
BootStrap Fileinput上传插件使用实例代码
Jul 28 Javascript
微信小程序获取手机系统信息的方法【附源码下载】
Dec 07 Javascript
vue-cli构建项目下使用微信分享功能
May 28 Javascript
使用p5.js实现动态GIF图片临摹重现
Oct 23 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下通过POST还是GET来传值
2008/06/05 PHP
PHP实现的英文名字全拼随机排号脚本
2014/07/04 PHP
PHP strtotime函数用法、实现原理和源码分析
2015/02/04 PHP
laravel多条件查询方法(and,or嵌套查询)
2019/10/09 PHP
Javascript优化技巧(文件瘦身篇)
2008/01/28 Javascript
escape、encodeURI 和 encodeURIComponent 的区别
2009/03/02 Javascript
一个可以兼容IE FF的加为首页与加入收藏实现代码
2009/11/02 Javascript
js 巧妙去除数组中的重复项
2010/01/25 Javascript
原生Js与jquery的多组处理, 仅展开一个区块的折叠效果
2011/01/09 Javascript
jQuery中bind,live,delegate与one方法的用法及区别解析
2013/12/30 Javascript
JSP基于Bootstrap分页显示实例解析
2016/06/12 Javascript
如何解决IONIC页面底部被遮住无法向上滚动问题
2016/09/06 Javascript
BootstrapValidator实现注册校验和登录错误提示效果
2017/03/10 Javascript
JavaScript 中的 this 简单规则
2017/09/19 Javascript
js实现复制功能(多种方法集合)
2018/01/06 Javascript
vue获取当前激活路由的方法
2018/03/17 Javascript
python 字符串split的用法分享
2013/03/23 Python
Python中的类与对象之描述符详解
2015/03/27 Python
在django中使用自定义标签实现分页功能
2017/07/04 Python
python中字符串变二维数组的实例讲解
2018/04/03 Python
Python实现朴素贝叶斯分类器的方法详解
2018/07/04 Python
pycharm+PyQt5+python最新开发环境配置(踩坑)
2019/02/11 Python
Spring实战之使用util:命名空间简化配置操作示例
2019/12/09 Python
Django关于admin的使用技巧和知识点
2020/02/10 Python
在python中实现求输出1-3+5-7+9-......101的和
2020/04/02 Python
关于Python3爬虫利器Appium的安装步骤
2020/07/29 Python
html5 Canvas画图教程(9)—canvas中画出矩形和圆形
2013/01/09 HTML / CSS
Nike墨西哥官网:Nike MX
2020/08/30 全球购物
幼儿园教师工作制度
2014/01/22 职场文书
小学优秀教师先进事迹材料
2014/12/16 职场文书
写给同学的新学期寄语
2015/02/27 职场文书
初中重阳节活动总结
2015/05/05 职场文书
幼儿园门卫安全责任书
2015/05/08 职场文书
征求意见函
2015/06/05 职场文书
车辆安全隐患排查制度
2015/08/05 职场文书
学生会副主席竞选稿
2015/11/19 职场文书