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 相关文章推荐
当鼠标移动到图片上时跟随鼠标显示放大的图片效果
Jun 06 Javascript
php和js对数据库图片进行等比缩放示例
Apr 28 Javascript
JavaScript italics方法入门实例(把字符串显示为斜体)
Oct 17 Javascript
javascript查询字符串参数的方法
Jan 28 Javascript
JS实现的数组全排列输出算法
Mar 19 Javascript
JS自定义函数对web前端上传的文件进行类型大小判断
Oct 19 Javascript
Vue.js实战之利用vue-router实现跳转页面
Apr 01 Javascript
微信扫码支付零云插件版实例详解
Apr 26 Javascript
Angular.js前台传list数组由后台spring MVC接收数组示例代码
Jul 31 Javascript
详解React Native 采用Fetch方式发送跨域POST请求
Nov 15 Javascript
js实现烟花特效
Mar 02 Javascript
浅谈nuxtjs校验登录中间件和混入(mixin)
Nov 06 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按行读取、处理较大CSV文件的代码实例
2014/04/09 PHP
php+mysql删除指定编号员工信息的方法
2015/01/14 PHP
php强大的时间转换函数strtotime
2016/02/18 PHP
用js来解决ajax读取页面乱码
2010/11/28 Javascript
一个轻量级的javascript库 pj介绍
2010/12/19 Javascript
一个JavaScript去除字符串末尾的空白实例代码
2014/09/22 Javascript
jQuery如何使用自动触发事件trigger
2015/11/29 Javascript
AngularJs Injecting Services Into Controllers详解
2016/09/02 Javascript
canvas绘图不清晰的解决方案
2017/02/28 Javascript
vuejs开发组件分享之H5图片上传、压缩及拍照旋转的问题处理
2017/03/06 Javascript
jQuery使用JSONP实现跨域获取数据的三种方法详解
2017/05/04 jQuery
Vue框架中正确引入JS库的方法介绍
2017/07/30 Javascript
js实现图片粘贴上传到服务器并展示的实例
2017/11/08 Javascript
nodejs中方法和模块用法示例
2018/12/24 NodeJs
深入理解使用Vue实现Context-Menu的思考与总结
2019/03/09 Javascript
Ajax请求时无法重定向的问题解决代码详解
2019/06/21 Javascript
微信小程序实现左滑动删除效果
2020/03/30 Javascript
vue:el-input输入时限制输入的类型操作
2020/08/05 Javascript
[36:02]DOTA2上海特级锦标赛D组小组赛#2 Liquid VS VP第一局
2016/02/28 DOTA
[01:18:33]Secret vs VGJ.S Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
Python记录详细调用堆栈日志的方法
2015/05/05 Python
python操作列表的函数使用代码详解
2017/12/28 Python
Python2实现的图片文本识别功能详解
2018/07/11 Python
python实现五子棋人机对战游戏
2020/03/25 Python
python利用re,bs4,requests模块获取股票数据
2019/07/29 Python
python性能测量工具cProfile使用解析
2019/09/26 Python
python KNN算法实现鸢尾花数据集分类
2019/10/24 Python
python 上下文管理器及自定义原理解析
2019/11/19 Python
利用pandas向一个csv文件追加写入数据的实现示例
2020/04/23 Python
瑞士国际航空官网:SWISS
2016/07/21 全球购物
四年级数学教学反思
2014/02/02 职场文书
大学生职业生涯规划书参考模板
2014/03/05 职场文书
口才训练演讲稿范文
2014/09/16 职场文书
《作风建设永远在路上》心得体会
2016/01/21 职场文书
你会写报告?产品体验报告到底该怎么写?
2019/08/14 职场文书
Pygame Time时间控制的具体使用详解
2021/11/17 Python