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 相关文章推荐
JS+flash实现chrome和ie浏览器下同时可以复制粘贴
Sep 22 Javascript
js定时器怎么写?就是在特定时间执行某段程序
Oct 11 Javascript
jquery实现简单文字提示效果
Dec 02 Javascript
Extjs实现下拉菜单效果
Apr 01 Javascript
Bootstrap 最常用的JS插件系列总结(图片轮播、标签切换等)
Jul 14 Javascript
jQuery弹出层插件popShow(改进版)用法示例
Jan 23 Javascript
js实现带进度条提示的多视频上传功能
Dec 13 Javascript
Jquery的Ajax技术使用方法
Jan 21 jQuery
如何优雅地在vue中添加权限控制示例详解
Mar 07 Javascript
基于Vue的商品主图放大镜方案详解
Sep 19 Javascript
Vue+Spring Boot简单用户登录(附Demo)
Nov 12 Javascript
javascript实现简单留言板案例
Feb 09 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 验证图片生成函数
2009/05/21 PHP
PHP使用strtotime计算两个给定日期之间天数的方法
2015/03/18 PHP
使用PHP如何实现高效安全的ftp服务器(一)
2015/12/20 PHP
javascript下IE与FF兼容函数收集
2008/09/17 Javascript
js 代码集(学习js的朋友可以看下)
2009/07/22 Javascript
理解JSON:3分钟课程
2011/10/28 Javascript
A标签触发onclick事件而不跳转的多种解决方法
2013/06/27 Javascript
jQuery插件windowScroll实现单屏滚动特效
2015/07/14 Javascript
jQuery Mobile开发中日期插件Mobiscroll使用说明
2016/03/02 Javascript
详述JavaScript实现继承的几种方式(推荐)
2016/03/22 Javascript
JavaScript类的写法
2016/09/17 Javascript
jQuery tagsinput在h5邮件客户端中应用详解
2016/09/26 Javascript
Node.js的Mongodb使用实例
2016/12/30 Javascript
js canvas实现擦除效果示例代码
2017/04/26 Javascript
详解Vue学习笔记入门篇之组件的内容分发(slot)
2017/07/17 Javascript
D3.js(v3)+react 实现带坐标与比例尺的散点图 (V3版本)
2019/05/09 Javascript
前端天气插件tpwidget使用方法详解
2019/06/24 Javascript
如何利用nodejs自动定时发送邮件提醒(超实用)
2020/12/01 NodeJs
[02:05]2014DOTA2西雅图国际邀请赛 BBC第二天小组赛总结
2014/07/11 DOTA
python不带重复的全排列代码
2013/08/13 Python
python实现将文本转换成语音的方法
2015/05/28 Python
python pandas读取csv后,获取列标签的方法
2018/11/12 Python
python sorted方法和列表使用解析
2019/11/18 Python
Python识别html主要文本框过程解析
2020/02/18 Python
详解Python3中的 input() 函数
2020/03/18 Python
Pytorch高阶OP操作where,gather原理
2020/04/30 Python
StubHub意大利:购买和出售全球演唱会和体育赛事门票
2017/11/21 全球购物
解释一下抽象方法和抽象类
2016/08/27 面试题
科级干部考察材料
2014/02/15 职场文书
家长对老师的感言
2014/03/11 职场文书
财务工作个人总结
2015/02/27 职场文书
企业安全隐患排查治理制度
2015/08/05 职场文书
Python基础数据类型tuple元组的概念与用法
2021/08/02 Python
mysql主从复制的实现步骤
2021/10/24 MySQL
oracle删除超过N天数据脚本的方法
2022/02/28 Oracle
MySQL数据库实验实现简单数据库应用系统设计
2022/06/21 MySQL