最近碰到了比较多的关于 vue 的 eventBus 的问题, 之前定技术选型的时候也被问到了, vuex 和 eventBus 的使用范围. 所以简单的写一下. 同时有一种特殊的实现方案.
有这么几种数据传递方式, vuex,props,eventBus 和特殊的 eventBus.
vuex
不介绍, 数据量和复杂度达不到不用它你才会向下看.
props
demo
父子组件传值, 官方 API, 只写个 demo.
1. 父组件
- <son :info="info" @update="updateHandler"/>
- // data
- info: 'sendToSon'
- // methods
- updateHandler (newVal) {
- this.info = newVal
- }
2. 子组件
- // props
- props: ['info']
- // 向上传值, 某个方法中使用
- this.$emit('update', 'got')
父向子传值 -->props 子向父传值 -->子组件绑定事件回调定义在父组件, 子组件触发此事件. 因不推荐子组件内直接修改父组件传入的 props, 需使用自定义事件.
限制
父子组件.
eventBus
demo
bus 皆为导入的 bus 实例
- // bus
- const bus = new Vue()
- // 数据接收组件
- // 当前组件接收值则
- bus.$on('event1', (val)=>{
- })
- // 数据发出组件
- // 当前组件发出值则
- bus.$emit('event1', val)
- \// 欢迎加入前端全栈开发交流圈一起学习交流: 864305860
可以看出本质是一个 vue 实例充当事件绑定的媒介. 在所有实例中使用其进行数据的通信.
双 (多) 方使用同名事件进行沟通.
问题
$emit 时, 必须已经 $on, 否则将无法监听到事件, 也就是说对组件是有一定的同时存在的要求的.(注: 路由切换时, 新路由组件先 created, 旧路由组件再 destoryed, 部分情况可以分别写入这两个生命周期, 见此问题).
$on 在组件销毁后不会自动解除绑定, 若同一组件多次生成则会多次绑定事件, 则会一次 $emit, 多次响应, 需额外处理.
数据非 "长效" 数据, 无法保存, 只在 $emit 后生效.
所以是否有一种更适用的方案呢?
特殊的 eventBus?
demo
我们先来看个代码, 线上代码. bus 皆为导入的 bus 实例.
- // bus
- const bus = new Vue({
- data () {
- return {
- // 定义数据
- val1: ''
- }// 欢迎加入前端全栈开发交流圈一起学习交流: 864305860
- },
- created () {
- // 绑定监听
- this.$on('updateData1', (val)=>{
- this.val1 = val
- })
- }
- })
- // 数据发出组件
- import bus from 'xx/bus'
- // 触发在 bus 中已经绑定好的事件
- bus.$emit('update1', '123')
- // 数据接收组件
- {{val1}}
- // 使用 computed 接收数据
- computed () {
- val1 () {
- // 依赖并返回 bus 中的 val1
- return bus.val1
- }
- }// 欢迎加入前端全栈开发交流圈一起学习交流: 864305860
不同
正统的 eventBus 只是用来绑定和触发事件, 并不关心数据, 不与数据发生交集. 而这个方案多一步将数据直接添加在 bus 实例上. 且事件监听与数据添加需提前定义好.
数据接收方不再使用 $on 来得知数据变化, 而是通过计算属性的特征被动接收.
解决的问题
通信组件需同时存在? 数据在 bus 上存储, 所以没有要求.
多次绑定? 绑定监听都在 bus 上, 不会重复绑定.
数据只在 $emit 后可用? 使用计算属性直接读取存在 bus 上的值, 不需要再次触发事件.
探讨
为什么使用计算属性
其实应该是为什么不能直接添加到 data 上, 如 data1: bus.data1? 我们可以再看一段代码, 线上代码. 将 bus 修改为
- data () {
- return {
- // 多一层结构
- val: {
- result: 0
- }// 欢迎加入前端全栈开发交流圈一起学习交流: 864305860
- }
- },
- created () {
- this.$on('update1', val => {
- console.log('触发 1', i1++)
- this.val.result = val
- })
- }
数据接收组件改为
// template
data 中获取直接修改值:{{dataResult}}
data 中获取直接修改值的父层:{{dataVal}}
computed 中依赖直接修改值:{{computedResult}}
- // JS// 欢迎加入前端全栈开发交流圈一起学习交流: 864305860
- data () {
- return {
- // 获取直接修改值
- dataResult: bus.val.result,
- // 获取直接修改值的父层
- dataVal: bus.val
- }
- },
- computed: {
- computedResult () {
- // 依赖直接修改值
- return bus.val.result
- }
- }// 欢迎加入前端全栈开发交流圈一起学习交流: 864305860
可以看到, data 中获取直接修改值值的数据是无法动态响应的.
为什么要用事件
其实不用 $emit 触发, 使用 bus.val = 1 直接赋值也是可以的, 那么为什么不这么做呢?
简化版的 vuex
其实这种 eventBus 就是简化版的 vuex. vue 文档中有这样一段话:
组件不允许直接修改属于 store 实例的 state, 而应执行 action 来分发 (dispatch) 事件通知 store 去改变, 我们最终达成了 Flux 架构. 这样约定的好处是, 我们能够记录所有 store 中发生的 state 改变.
store 对应 bus 实例, state 对应 data, action 对应 事件, dispatch 对应 $emit. 同时 vuex 中组件获取数据的方式正是通过计算属性, 那么其实 vuex 和 Flux 架构的理解和使用也没有那么难不是吗.
结语
感谢您的观看, 如有不足之处, 欢迎批评指正.
来源: http://www.qdfuns.com/article/51117/16db9bbb01abf89c10d118c6afbeee49.html