FBI Warning:本文是记录本菜鸟学习bus通信的一次记录。如大佬熟练掌握Vue或已对Vue源码有了解,不建议浪费时间阅读。部分文字可能引起感官上的不适,请在男/女朋友的陪同下阅读
1.整体思路
看了官网文档关于bus的用法,遂想写个简单的组件bus通讯demo练手。
- showTime组件监听bus的自定义事件,并对传递的参数做处理
- getTime组件通过bus触发自定义事件
2. 相关代码
上面的思路对应代码如下。为方便直接用vue-cli构建
2.1 showTime.vue
<template>
<div >
<p>时间:{{time}}</p>
</div>
</template>
<script>
import bus from './bus'
export default {
data () {
return {
time: '初始值'
}
},
created() {
bus.$on('get', data => {
console.log(data)
this.time= data
})
}
}
</script>
2.2 getTime.vue
<template>
<div>
<p>我是组件b</p>
<button @click="getTime">发送时间给组件a</button>
</div>
</template>
<script>
import bus from './bus'
export default {
methods: {
getTime() {
bus.$emit('get', `${new Date()}`)
}
}
}
</script>
2.3 bus.js
import Vue from 'vue'
export default new Vue()
2.4 路由
import Vue from 'vue'
import Router from 'vue-router'
import getTime from '@/components/getTime '
import showTime from '@/components/showTime'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: showTime
},
{
path: '/getTime',
component: getTime
}
]
})
3. 出现问题
3.1 操作步骤
- 先进入showTime组件,让bus开始监听自定义的事件
- 然后进入getTime组件,通过点击按钮触发事件
- 理想情况下,再次进入showTime组件,会更新time的值,并更新视图
3.2 问题概述
- showTime组件视图并没有如期更新
- 多次重复以上步骤,第n次进入后点击按钮,会执行n次
console.log(data)
4. debug思路
- 通过观察,发现bus的get事件在getTime组件的按钮按下后,在触发bus的get事件时就已经将参数
new Date()
发送出去了,而非进入showTime组件时。 - 这个现象纠正了我之前对bus通讯一个错误的认识:之前我以为bus是在进入showTime的created钩子时,才会响应get事件并传递参数。事实是,showTime组件只是提供了为 bus 注册事件的时机。一旦注册,在组件层面上 bus 已经跟showTime组件没关系了。
- 3.1的三个操作步骤,实质上发生了以下事情:
- bus开始监听get事件
- showTime组件销毁。在getTime组件bus触发get事件,传递参数
new Date()
。 - getTime组件销毁,在created钩子,time值初始化为'初始值'(这一步中,get事件传递参数的时机早于getTime组件的created钩子,所以视图不会更新);同时bus再次监听一个新的get事件(为什么会说再次呢?接下来会解释)
- 关于上文提到的bus再次监听一个新的事件,我们先将showTime组件的created钩子添加调试代码,
通过观察bus._events的变化来理解
bus.$on('get', data => {
console.log(data)
this.time= data
})
console.log(bus._events) //
- bus._events是一个对象。当进入showTime的created钩子,会为bus._events添加一个key为get,value为空数组,并将监听get事件的回调函数添加至这个数组。因此,n次进入showTime组件后,这个数组就会添加n个回调函数。每一次在getTime组件按下按钮,都会执行数组里的每一个回调函数,造成n次切换路由后点击按钮,bus监听自定义函数的代码会被执行n遍
5. 解决方案
- 从上面的分析中可以看出,出现的2个问题都是和组件的created钩子有关。所以可以通过使用
keep-alive
缓存组件,除了第一次进入showTime组件会触发created生命周期,之后切换路由都不会销毁组件。因此之后进入showTime组件,time
都会被更新为传递的参数,也不会往bus._events添加多个自定义事件回调函数 - 但感觉这样似乎有点复杂,如果对于非兄弟非父子组件的Bus通讯有更好的用法,更深的理解,欢迎一起探讨。水平有限,如文章有说错的地方,也欢迎指正。