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_13_执行模型详解
Oct 20 Javascript
JQuery加载图片自适应固定大小的DIV
Sep 12 Javascript
js将字符串转成正则表达式的实现方法
Nov 13 Javascript
jquery查找tr td 示例模拟
May 08 Javascript
JavaScript常用脚本汇总(一)
Mar 04 Javascript
bootstrap select下拉搜索插件使用方法详解
Nov 23 Javascript
详解在React里使用&quot;Vuex&quot;
Apr 02 Javascript
详解angular部署到iis出现404解决方案
Aug 14 Javascript
Vue模拟数据,实现路由进入商品详情页面的示例
Aug 31 Javascript
小程序多图列表实现性能优化的方法步骤
May 28 Javascript
JavaScript中的ES6 Proxy的具体使用
Jun 16 Javascript
antd多选下拉框一行展示的实现方式
Oct 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
php中判断字符串是否全是中文或含有中文的实现代码
2011/09/16 PHP
PHP GD库生成图像的几个函数总结
2014/11/19 PHP
详解PHP中的Traits
2015/07/29 PHP
Laravel框架实现发送短信验证功能代码
2016/06/06 PHP
PHP实现十进制数字与二十六进制字母串相互转换操作示例
2018/08/10 PHP
laravel框架使用阿里云短信发送消息操作示例
2020/02/15 PHP
记录几个javascript有关的小细节
2007/04/02 Javascript
一个选择最快的服务器转向代码
2009/04/27 Javascript
JavaScript 撑出页面文字换行
2009/06/15 Javascript
js读取本地excel文档数据的代码
2010/11/11 Javascript
js获取 type=radio 值的方法
2014/05/09 Javascript
js实现遍历含有input的table实例
2015/12/07 Javascript
Express实现前端后端通信上传图片之存储数据库(mysql)傻瓜式教程(二)
2015/12/10 Javascript
jquery+json实现分页效果
2016/03/07 Javascript
jquery.multiselect多选下拉框实现代码
2016/11/11 Javascript
js addDqmForPP给标签内属性值加上双引号的函数
2016/12/24 Javascript
jQuery接受后台传递的List的实例详解
2017/08/02 jQuery
一步步教你利用webpack如何搭一个vue脚手架(超详细讲解和注释)
2018/01/08 Javascript
vue返回上一页面时回到原先滚动的位置的方法
2018/12/20 Javascript
JavaScript遍历数组的三种方法map、forEach与filter实例详解
2019/02/27 Javascript
微信小程序下拉加载和上拉刷新两种实现方法详解
2019/09/05 Javascript
es6函数之尾递归用法实例分析
2020/04/25 Javascript
python绘图库Matplotlib的安装
2014/07/03 Python
python退出命令是什么?详解python退出方法
2018/12/10 Python
Python 安装第三方库 pip install 安装慢安装不上的解决办法
2019/06/18 Python
Python实现制度转换(货币,温度,长度)
2019/07/14 Python
PyCharm 无法 import pandas 程序卡住的解决方式
2020/03/09 Python
Python 如何定义匿名或内联函数
2020/08/01 Python
Python监听键盘和鼠标事件的示例代码
2020/11/18 Python
美国领先的礼品卡网站:GiftCards.com
2016/11/02 全球购物
Java编程面试题
2016/04/04 面试题
班组长岗位职责范本
2014/01/05 职场文书
小学语文教学反思
2014/02/10 职场文书
公司运动会策划方案
2014/05/25 职场文书
精神文明建设汇报材料
2014/12/24 职场文书
2015年学校总务工作总结
2015/07/20 职场文书