vue中的eventBus会不会产生内存泄漏你知道吗

目录
  • 引入
  • 内容
  • eventBus在vue中的实现
  • 例子:使用eventBus
  • 例子:不及时取消订阅
  • 其它注意事项
  • 总结

eventBus是在vue中经常用来解决跨组件消息传递的问题,但对它的使用要特别注意,否则会产生很严重的后果。

引入

本文介绍了eventBus的实现原理,并介绍它如何在vue中使用,并举了一个具体的例子来说明,如果使用不当,它会造成内存泄漏。

要注意eventBus并不是前端的概念。

由greenrobot [1] 组织贡献(该组织还贡献了greenDAO),一个Android事件发布/订阅轻量级框架,

功能:通过解耦发布者和订阅者简化Android事件传递 [2]

EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment,Activity,Service线程之间传递数据,执行方法。

特点:代码简洁,是一种发布订阅设计模式(观察者设计模式)。

内容

  • eventBus在vue中的实现;
  • 在vue使用eventBus;
  • 使用不当的问题:多次执行回调;内存泄漏;
  • 解决方案:及时调用$off

eventBus在vue中的实现

eventBus是事件总线的意思,它本质上是一个发布订阅者实现,在vue2.X中,vue实例上提供了$on,$emit,$off这三个方法,分别用来添加观察者,发布事件,取消订阅这三个操作。

所以,我们可以直接把一个vue实例挂到Vue的原型上来充当组件相互通信的中介。

Vue.prototype.$eventBus = new Vue()

这样一来,所有的Vue组件都可以沿着原型链找到这个$eventBus,从而访问$on, $off,$emit。

它可以帮助我们实现跨组件的通信。

例子:使用eventBus

在根组件中发布事件,在两个子组件中去监听事件。

<div id="app">
   <h2>eventBus的基本使用</h2>
   <com1></com1>
   <com2></com2>
 </div>
 <script>
   Vue.prototype.$eventBus = new Vue()
   Vue.component('com1', {
     template:`<div>com1</div>`,
     created () {
       this.$eventBus.$on('event1', function f1(d){
         conse.log(d, 'com1 listen...  event1')
       })
     },
   })
   Vue.component('com2', {
     template:`<div>com2</div>`,
     created () {
       this.$eventBus.$on('event2', function f2(d) {
         conse.log(d, 'com2 listen...  event2')
       })
     }
   })
   var vm = new Vue({
     el: '#app',
     created () {
       setInterval( () => {
         const d = Date.now()
         this.$eventBus.$emit('event1', d)
         this.$eventBus.$emit('event2', d)
       }, 3000)
     }
   })
 </script>

在创建com1组件时,订阅event1事件;在创建com2组件时,订阅event2事件;在创建根组件(vue实例)时,开启定时器:每隔3s发布事件,这样的话,com1和com2就都可以收到事件,并执行对应的回调。

效果如下:

例子:不及时取消订阅

如果不及时取消订阅,则回调函数仍会执行,更严重的是,如果在事件处理回调函数中引用了外部变量形成了闭包,则会导致内存泄漏。

下面的代码说明这个问题。

在根组件(vue实例)中,补充一个数据项showCom1,并配置v-if指令来实现销毁和重建com1组件。

<div id="app">
   <h2>不及时取消订阅的问题</h2>
   <button @click="showCom1=!showCom1">
     {{showCom1 ? "销毁" : "重建"}}组件1
   </button>
   <com1 v-if="showCom1"></com1>
   <com2></com2>
 </div>
 <script>
   Vue.prototype.$eventBus = new Vue()
   Vue.component('com1', {
     template:`<div>com1</div>`,
     created () {
       conse.log('创建com1')
       this.$eventBus.$on('event1', function f1(d) {
         conse.log(d, 'com1 listen... event1')
       })
     }
   })
   Vue.component('com2', {
     template:`<div>com2</div>`,
     created () {
       this.$eventBus.$on('event2', function f2(d) {
         conse.log(d, 'com2 listen... event2')
       })
     }
   })
   var vm = new Vue({
     el: '#app',
     data:{
       showCom1: true
     },
     created () {
       setInterval( () => {
         const d = Date.now()
         this.$eventBus.$emit('event1', d)
         this.$eventBus.$emit('event2', d)
       }, 3000)
     }
   })
 </script>

先提一个问题:你觉得com1组件被销毁后,它在created中订阅的event1事件还能再收到吗?对应的回调函数还能再执行吗?一般的想法是组件都销毁了,那它订阅的事件肯定也收不到了嘛

答案是:还能收到。原因很简单:事件订阅这功能是$eventBus对象完成的,与这个com1组件无关。

上面的代码执行的效果,是这样的:

  • 销毁组件1之后,它还能正常收到event1事件,并执行回调;
  • 再次创建组件1后,它会再次订阅event1事件,所以结果是执行两次回调。

下面再来说明内存泄漏的问题,把com1的组件内容改成如下:

 Vue.component('com1', {
   template:`<div>com1</div>`,
   created () {
     console.log('创建com1')
     let m = 1*1024 * 1024
     let arr = new Array(m).fill('a')

     this.$eventBus.$on('event1', function f1(d) {
       // 注意这里有一个闭包
       console.log(d, 'com1 listen... event1', arr[1])
    })
  }
 })

在回调函数f1中引用函数之外的变量arr,这里有一个闭包。

下面在浏览器的调试工具中的memory添加一个快照,查看结果如下:

然后,点击页面上的“销毁组件1”,再次添加一个快照,你会发现这个空间并没有释放掉。

解释如下:

上面是这个过程的示意图,由于没有及时取消订阅f1,所以arr这个数组并没有释放掉。

解决方案:

在com1的destoryed钩子中,调用$off来取消订阅。

destroyed () {
   // 取消所有对event1事件的监听
   this.$eventBus.$off('event1')
 }

调试结果如下:

可见,com1删除之后,这个数值的空间释放掉了,同时它的事件监听函数也不会再执行了。

其它注意事项

$off的格式:

  • $off() 会取消所有的事件订阅;
  • $off('事件名') 会取消指定事件名的;
  • $off('事件名', 回调) 会取消指定事件名的,指定回调

父子组件的created和mounted的区别, 按执行顺序:

  • 父组件的created 先于子组件的created
  • 父组件的mounted先于子组件的mounted

所以,到底在哪个钩子中订阅,在哪个钩子中发布,要根据情况来定。

总结

eventBus是一个名词,并非前端独有;

new Vue() 得到的实例上已经实现了发布订阅模式,可以直接做eventBus使用;

使用eventBus要及时调用$off;

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Vue优化:常见会导致内存泄漏问题及优化详解

    如果你在用 Vue 开发应用,那么就要当心内存泄漏的问题.这个问题在单页应用 (SPA) 中尤为重要,因为在 SPA 的设计中,用户使用它时是不需要刷新浏览器的,所以 JavaScript 应用需要自行清理组件来确保垃圾回收以预期的方式生效.因此在vue开发过程中,你需要时刻警惕内存泄漏的问题,这些内存泄漏往往会发生在使用 Vue 之外的其它进行 DOM 操作的三方库时,请确保测试应用的内存泄漏问题并在适当的时机做必要的组件清理. 下面是我开发过程中遇到,并查资料总结的内存泄漏问题,会持续更新中

  • electron-vue开发环境内存泄漏问题汇总

    package.json "dependencies": { "vue": "^2.5.16" }, "devDependencies": { "ajv": "^6.5.0", "babel-core": "^6.26.3", "babel-loader": "^7.1.4", "babel-plu

  • 解决vue自定义指令导致的内存泄漏问题

    vue的自定义指令是一个比较容易引起内存泄漏的地方,原因就在于指令通常给元素绑定了事件,但是如果忘记了解绑,就会产生内存泄漏的问题. 看下面代码: directives: { scroll: { inserted (el, cb) { // 不是元素节点 || 未设置回调函数 if (el.nodeType !== 1 || !cb) return let direct = 'down' let rollHeight = 0 let getScrollEventTarget = (target)

  • vue使用中的内存泄漏【推荐】

    今天看到一篇关于js使用中内存泄露的文章,以及chrom浏览器查看内存泄漏的方法,决定留着.本文只截取了我认为比较重要的部分,喜欢原文的小伙伴,请点击文章下方的原文链接. 什么是内存泄露?内存泄露是指new了一块内存,但无法被释放或者被垃圾回收.new了一个对象之后,它申请占用了一块堆内存,当把这个对象指针置为null时或者离开作用域导致被销毁,那么这块内存没有人引用它了在JS里面就会被自动垃圾回收.但是如果这个对象指针没有被置为null,且代码里面没办法再获取到这个对象指针了,就会导致无法释放

  • 浅谈VueJS SSR 后端绘制内存泄漏的相关解决经验

    引言 Memory Leak 是最难排查调试的 Bug 种类之一,因为内存泄漏是个 undecidable problem,只有开发者才能明确一块内存是不是需要被回收.再加上内存泄漏也没有特定的报错信息,只能通过一定时间段的日志来判断是否存在内存泄漏.大家熟悉的常用调试工具对排查内存泄漏也没有用武之地.当然了,除了专门用于排查内存泄漏的工具(抓取Heap之类的工具)之外. 对于不同的语言,各种排查内存泄漏的方式方法也不尽相同.对于 JavaScript 来说,针对不同的平台,调试工具也是不一样的

  • vue中的eventBus会不会产生内存泄漏你知道吗

    目录 引入 内容 eventBus在vue中的实现 例子:使用eventBus 例子:不及时取消订阅 其它注意事项 总结 eventBus是在vue中经常用来解决跨组件消息传递的问题,但对它的使用要特别注意,否则会产生很严重的后果. 引入 本文介绍了eventBus的实现原理,并介绍它如何在vue中使用,并举了一个具体的例子来说明,如果使用不当,它会造成内存泄漏. 要注意eventBus并不是前端的概念. 由greenrobot [1] 组织贡献(该组织还贡献了greenDAO),一个Andro

  • vue中eventbus被多次触发以及踩过的坑

    一开始的需求是这样子的,我为了实现两个页面组件之间的数据传递,假设我有页面A,点击页面A上的某一个按钮之后,页面会自动跳转到页面B,同时我希望将页面A上的某一些参数携带过去给页面B.(我知道,小参数的时候可以通过路由的params或者query去传参数,或者大型数据可以用vuex来处理,很遗憾我到现在还没有做很大型的项目,所以还没有用过vuex,接下来会学习一下.) 然后我就想,这不就是不同组件之间的数据传递问题而已吗?直接用bus 巴士事件来传递数据不就行了吗.于是,我就很愉快地进行了.关于v

  • Android中内存泄漏需要的注意点

    内存泄漏对每一位 Android 开发一定是司空见惯,大家或多或少都肯定有些许接触.大家都知道,每一个手机都有一定的承载上限,多处的内存泄漏堆积一定会堆积如山,最终出现内存爆炸 OOM. 而这,也是极有可能在 Android 面试中一道常见的开放题. 内存泄漏的根本原因是一个长生命周期的对象持有了一个短生命周期的对象.如果你对垃圾回收机制有所了解,我想这个问题基本难不住你,因为知道了原理,自然不会去触碰这些极易导致内存泄漏的雷区. 该题重在积累,不需要死记硬背,自己多总结即可. 1. 长生命周期

  • vue的keep-alive中使用EventBus的方法

    最近项目中由于列表居多且都做了下拉刷新上拉加载,所以就使用了keep-alive组件来缓存页面数据,但是当在其他页面做一些操作改变了列表数据,当来到列表的时候每次都要下拉刷新一下,体验不好,所以就使用到了EventBus,在需要更新的时候就用EventBus来刷新列表. 1.keep-alive组件 keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能,由于是一个抽象组件,所以在页面渲染完毕后不会被渲染成一个DOM元素.但是对于展示型页面比较好用,表单类页面也会将所填

  • Vue中使用的EventBus有生命周期

    最近遇到了vue项目中的性能问题,整个项目不断的进行操作五分钟左右,页面已经很卡,查看页面占用了1.5G内存,经过排查一部分原因,是自己模块使用的eventBus在离开页面未进行off掉.我们进行下验证: 1.不随生命周期销毁 我们在home首页的代码是这样的: created () { let text = Array(1000000).fill('xxx').join(',') //创建一个大的字符串用于验证内存占用 $eventBus.$on('home-on', (...args) =>

  • vue组件中传值EventBus的使用及注意事项说明

    主要想说下非父子组件之间的通信. 项目场景: 在app.vue里写了一个公共的顶部导航navbar,然后右侧有个分享按钮,而这个分享按钮只有在特定的页面才展示,项目里是在lottery.vue页面,然后想实现app.vue里点击分享按钮,触发lottery.vue里的分享方法. 解决:使用eventBus 1.创建一个event-bus.js import Vue from 'vue' export const EventBus = new Vue() 2.在app.vue引入eventbus,

  • Vue中事件总线(eventBus)的深入详解及使用

    目录 1.简介 2.使用 安装及引入 在组件中使用 补充:移除监听事件 总结 1. 简介 Vue 组件中常见的有:父子组件通信.兄弟组件通信.而父子组件通信就很简单,父组件会通过 props 向下传数据给子组件,当子组件有事情要告诉父组件时会通过 $emit 事件告诉父组件. 今天就来说说,如果两个页面没有任何引入和被引入关系,该如何通信呢? 如果应用程序不需要类似 Vuex 这样的库来处理组件之间的数据通信,就可以考虑 Vue 中的事件总线 ,即 eventBus 来通信. eventBus

  • vue中组件通信的八种方式(值得收藏!)

    前言 之前写了一篇关于vue面试总结的文章, 有不少网友提出组件之间通信方式还有很多, 这篇文章便是专门总结组件之间通信的 vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么组件之间如何进行数据通信的呢? 首先我们需要知道在vue中组件之间存在什么样的关系, 才更容易理解他们的通信方式, 就好像过年回家,坐着一屋子的陌生人,相互之间怎么称呼,这时就需要先知道自己和他们之间是什么样的关系. vue组件中关系说明: 如上图所示, A与B.A与C.B与D.C与E组件之间

  • 详解Vue中组件传值的多重实现方式

    vue中组件传值方式整理 1.我们最熟悉的父子组件传值 父->子 props 子->父 $emit 这些我们最常用的就不说了,如果有不理解的,俺这儿有篇不错的文章,链接奉上~ https://www.cnblogs.com/LoveAndPeace/p/7273648.html 2.兄弟组件或无关系组件之间的传值 我们最常用的方式采用一个第三方变量 俗称eventbus 通过其中一个$emit发另一个$on接收的方式 实现组件传值 main.js中挂载一下 在其中一个页面$emit 另一个页面

  • vue中各种通信传值方式总结

    1.路由通信传值 路由通信是通过路由跳转用query把参数带过去,也是vue常用的通信手段. 例子:创建并在路由注册一个组件Head <template> <div id="head"> <button @click="handleChange">clickMe</button> //给按钮绑定点击事件 </div> </template> <script> export defau

随机推荐