Vue2【Vue基础十】— 兄弟组件之间传值【中央事件总线,消息订阅与发布】
Vue2 : 【Vue基础九】–父子组件传值
一、 兄弟组件
1-1 事件总线
- 使用Vue3,会发现,原本得心应手的eventBus突然不灵了
- Vue3不再提供$on与emit函数,Vue实例不再实现事件接口。官方推荐引入外部工具实现,或者自己手撸一个事件类
1-1-1 使用EventBus
- 引入/编写事件库
- 在入口文件中挂载
- 在组件中引入并使用
1-1-2 不借助插件的原生使用方式
-
引入/编写事件库
- 方法一: 引入官方推荐的mitt
- 方法二: 手撸一个简单的发布/订阅类
// eventBus.js export default class EventBus{ constructor(){ this.events = {}; } emit(eventName, data) { if (this.events[eventName]) { this.events[eventName].forEach(function(fn) { fn(data); }); } } on(eventName, fn) { this.events[eventName] = this.events[eventName] || []; this.events[eventName].push(fn); } off(eventName, fn) { if (this.events[eventName]) { for (var i = 0; i < this.events[eventName].length; i++) { if (this.events[eventName][i] === fn) { this.events[eventName].splice(i, 1); break; } }; } } }
-
在入口文件main.js中执行挂载
// main.js
import { createApp } from 'vue'
import App from './App.vue'
// ① 引入事件类
// 自己编写的或者mitt皆可
import EventBus from 'lib/bus.js'
// 或者:import EventBus from 'mitt'
const $bus = new EventBus()
// ② 挂载
// 1.使用provide提供
app.provide('$bus', $bus)
// 2.挂载到this上
app.config.globalProperties.$bus = $bus
- 在组件中引入并使用
- 在creates中使用
// Button.vue export default { created() { this.$bus.emit('ButtonCreated') } }
- 在setup中使用
注意: 因为在setup中无法访问到应用实例(this),如果你需要在setup中使用eventBus,则需要通过provide/inject方式引入
// Button.vue import { inject } from 'vue' export default { setup() { const $bus = inject('$bus') $bus.emit('ButtonSetup') } }
二、 爷孙组件
2-1 props和$emit
最常用的父子组件通信方式,爷爷–> 父 ----> 子
1、 父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件做到的
2、 处理父子组件之间的数据传输有一个问题:
- 多层嵌套,父组件A下面有子组件B,组件B下面有组件C,A想传递给C怎么办?
- 采用第一种方法,必须让组件A通过prop传递消息给组件B,组件B再传给C
- Vue2.4之后,引入了attrs和listeners来解决这个问题
2-2 attrs和listeners
1、 attrs和listeners的过程:
父组件A下面有子组件B,组件B下面有组件C,组件A传递数据给组件B
- C组件
Vue.component('C', {
template: `
<div>
<input type="text" v-model="$attrs.messageC" @input="passCData($attrs.messageC)">
</div>
`,
methods: {
passCData(val) {
// 触发父组件A中的事件
this.$emit('getCData',val)
}
}
})
- B组件
Vue.component('B', {
data() {
return {
myMessage: this.message
}
},
template: `
<div>
<input type="text" v-model="myMessage" @input="passData(myMessage)">
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
`
// 得到父组件传递过来的数据
props: ['message'],
methods: {
passData(val) {
// 触发父组件中的事件
this.$emit('getChildData',val)
}
}
})
- A组件
Vue.component('A',{
template: `
<div>
<p>this is parent compoent!</p>
<B
:messageC="messageC"
:message="message"
v-on:getCData="getCData"
v-on:getChildData="getChildData(message)">
</B>
</div>
`,
data() {
return {
message: 'Hello',
messageC: 'Hello c'
}
},
methods: {
getChildData(val) {
console.log('这是来自B组件的数据')
},
// 执行C子组件触发的事件
getCData(val) {
console.log("这是来自C组建的数据:"+val)
}
}
})
var app = new Vue({
el: '#app',
template: `
<div><A></A></div>
`
})
2、 解析:
- C组件中能直接触发getData的原因: B组件调用C组件时,使用v-on绑定了$listeners属性
- 通过v-bind绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的)
三、 v-model
1、 父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值
后续回来得看看
- https://blog.csdn.net/m0_56986233/article/details/121405388
- https://www.zhihu.com/question/466846675