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 迁移目录
Dec 18 Javascript
Javascript 类与静态类的实现(续)
Apr 02 Javascript
jquery插件jquery倒计时插件分享
Dec 27 Javascript
Bootstrap Chart组件使用教程
Apr 28 Javascript
JavaScript面试题大全(推荐)
Sep 22 Javascript
jQuery鼠标事件总结
Oct 13 Javascript
jquery配合.NET实现点击指定绑定数据并且能够一键下载
Oct 28 Javascript
JS实现双击内容变为可编辑状态
Mar 03 Javascript
js图片加载效果实例代码(延迟加载+瀑布流加载)
May 12 Javascript
vue2.0 实现页面导航提示引导的方法
Mar 13 Javascript
axios的拦截请求与响应方法
Aug 11 Javascript
JavaScript中的Proxy对象
Nov 27 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
咖啡店都有些什么常规豆子呢?有什么风味在里面
2021/03/04 咖啡文化
PHP实现的功能是显示8条基色色带
2006/10/09 PHP
PHP将整个网站生成HTML纯静态网页的方法总结
2012/02/05 PHP
js切换div css注意的细节
2012/12/10 Javascript
实例讲解JS中数组Array的操作方法
2014/05/09 Javascript
jquery easyui使用心得
2014/07/07 Javascript
JS简单实现城市二级联动选择插件的方法
2015/08/19 Javascript
原生js实现addClass,removeClass,hasClass方法
2016/04/27 Javascript
简单理解vue中el、template、replace元素
2016/10/27 Javascript
vue-cli如何快速构建vue项目
2017/04/26 Javascript
vue2.0实现分页组件的实例代码
2017/06/22 Javascript
JS实现仿UC浏览器前进后退效果的实例代码
2017/07/17 Javascript
vue slot 在子组件中显示父组件传递的模板
2018/03/02 Javascript
微信小程序地图(map)组件点击(tap)获取经纬度的方法
2019/01/10 Javascript
Vue router安装及使用方法解析
2020/12/02 Vue.js
python with statement 进行文件操作指南
2014/08/22 Python
Python中正则表达式详解
2017/05/17 Python
基于Python代码编辑器的选用(详解)
2017/09/13 Python
[原创]windows下Anaconda的安装与配置正解(Anaconda入门教程)
2018/04/05 Python
Python实现的特征提取操作示例
2018/12/03 Python
python实现石头剪刀布小游戏
2021/01/20 Python
python替换字符串中的子串图文步骤
2019/06/19 Python
python写程序统计词频的方法
2019/07/29 Python
Python判断字符串是否为合法标示符操作
2020/09/03 Python
一款纯css3实现的响应式导航
2014/10/31 HTML / CSS
SCDKey德国:全球领先的数字游戏市场
2019/04/09 全球购物
工厂仓管员岗位职责范本
2014/07/17 职场文书
安全目标责任书
2014/07/22 职场文书
股东出资证明书范例
2014/10/04 职场文书
商铺租房协议书范本
2014/12/04 职场文书
五一劳动节慰问信
2015/02/14 职场文书
房地产项目合作意向书
2015/05/08 职场文书
教师节联欢会主持词
2015/07/04 职场文书
如何理解Vue前后端数据交互与显示
2021/05/10 Vue.js
Android开发之WECHAT微信小程序路由跳转的两种形式
2022/04/12 Java/Android
聊聊mysql都有哪几种分区方式
2022/04/13 MySQL