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 相关文章推荐
一种JavaScript的设计模式
Nov 22 Javascript
JavaScript 事件对象的实现
Jul 13 Javascript
js中document.getElementByid、document.all和document.layers区分介绍
Dec 08 Javascript
JS字符串累加Array不一定比字符串累加快(根据电脑配置)
May 14 Javascript
JavaScript 浏览器对象模型BOM使用介绍
Apr 13 Javascript
javascript实现的图片切割多块效果实例
May 07 Javascript
javascript实现检验的各种规则
Jul 31 Javascript
基于javascript实现图片滑动效果
May 07 Javascript
js 动态生成json对象、时时更新json对象的方法
Dec 02 Javascript
JS和canvas实现俄罗斯方块
Mar 14 Javascript
Vue-cli-webpack搭建斗鱼直播步骤详解
Nov 17 Javascript
基于casperjs和resemble.js实现一个像素对比服务详解
Jan 10 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
使用sockets:从新闻组中获取文章(三)
2006/10/09 PHP
php array_merge下进行数组合并的代码
2008/07/22 PHP
PHP提取数据库内容中的图片地址并循环输出
2010/03/21 PHP
浅析PHP原理之变量分离/引用(Variables Separation)
2013/08/09 PHP
php中遍历二维数组并以表格的形式输出的方法
2017/01/03 PHP
Yii2选项卡的简单使用
2017/05/26 PHP
laravel orm 关联条件查询代码
2019/10/21 PHP
javascript 判断数组是否已包含了某个元素的函数
2010/05/30 Javascript
在chrome浏览器中,防止input[text]和textarea在聚焦时出现黄色边框的解决方法
2011/05/24 Javascript
JS实现标签页效果(配合css)
2013/04/03 Javascript
解析Jquery中如何把一段html代码动态写入到DIV中(实例说明)
2013/07/09 Javascript
javascript中interval与setTimeOut的区别示例介绍
2014/03/14 Javascript
浅谈JavaScript Math和Number对象
2015/01/26 Javascript
jQuery Easyui学习教程之实现datagrid在没有数据时显示相关提示内容
2016/07/09 Javascript
JS生成和下载二维码的代码
2016/12/07 Javascript
JS判断键盘是否按的回车键并触发指定按钮点击操作的方法
2017/02/13 Javascript
微信小程序 标签传入数据
2017/05/08 Javascript
Bootstrap实现可折叠分组侧边导航菜单
2018/03/07 Javascript
JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例
2018/07/27 Javascript
Vue.js 通过jQuery ajax获取数据实现更新后重新渲染页面的方法
2018/08/09 jQuery
ng-events类似ionic中Events的angular全局事件
2018/09/05 Javascript
记一次用ts+vuecli4重构项目的实现
2020/05/21 Javascript
Django框架下在URLconf中指定视图缓存的方法
2015/07/23 Python
python3.6的venv模块使用详解
2018/08/01 Python
numpy 声明空数组详解
2019/12/05 Python
解决jupyter notebook显示不全出现框框或者乱码问题
2020/04/09 Python
CSS3 3D酷炫立方体变换动画的实现
2019/03/26 HTML / CSS
Expedia印度尼西亚站:预订酒店、廉价航班和度假套餐
2018/01/31 全球购物
利达恒信公司.NET笔试题面试题
2016/03/05 面试题
青年教师培训方案
2014/02/06 职场文书
小学二年级学生评语
2014/04/21 职场文书
运动会方队口号
2014/06/07 职场文书
教师教育教学随笔
2015/08/15 职场文书
Java tomcat手动配置servlet详解
2021/11/27 Java/Android
JS前端可视化canvas动画原理及其推导实现
2022/08/05 Javascript
MySQL中dd::columns表结构转table过程及应用详解
2022/09/23 MySQL