Vue组件间通信的方式
组件间通信的分类:
- 父子组件之间的通信
- 兄弟组件之间的通信
- 祖孙与后代组件之间的通信
- 非关系组件间之间的通信
组件间通信的方案:
- 通过 props 传递
- 通过 $emit 触发自定义事件
- 使用 ref
- EventBus
- 通过 $parent 或 $root
- attrs 与listeners
- Provide 与 Inject
- Vuex/Pinia
EventBus
- 使用场景:兄弟组件传值
- 创建一个中央事件总线EventBus
- 兄弟组件通过 e m i t 触发自定义事件, emit触发自定义事件, emit触发自定义事件,emit第二个参数为传递的数值
- 另一个兄弟组件通过$on监听自定义事件
// 创建一个中央时间总线类
class Bus {
constructor() {
this.callbacks = {}; // 存放事件的名字
}
$on(name, fn) {
this.callbacks[name] = this.callbacks[name] || [];
this.callbacks[name].push(fn);
}
$emit(name, args) {
if (this.callbacks[name]) {
this.callbacks[name].forEach((cb) => cb(args));
}
}
}
// main.js
Vue.prototype.$bus = new Bus() // 将$bus挂载到vue实例的原型上
// 另一种方式
Vue.prototype.$bus = new Vue() // Vue已经实现了Bus的功能
// 组件1
this.$bus.$emit('foo')
// 组件2
this.$bus.$on('foo', this.handle)
$parent 或 $root
通过共同祖辈$parent或者 $root搭建通信桥连
兄弟组件1:
this. $parent.on(‘add’,this.add)
兄弟组件2:
this. $parent.emit(‘add’)
$attrs 与 $ listeners
- 适用场景:祖先传递数据给子孙
- 设置批量向下传属性$attrs和 $listeners
- 包含了父级作用域中不作为 prop 被识别,(且获取) 的特性绑定 ( class 和 style 除外)。
- 可以通过 v-bind=“$attrs” 传⼊内部组件
实现原理
属性传递:爷爷组件将一些属性传递给父组件,父组件通过 $attrs 将这些属性传递给孙子组件。
事件监听器传递:爷爷组件绑定了一些事件监听器到父组件,父组件通过 $listeners 将这些监听器传递给孙子组件。
vue2.x 使用
// grandpa.vue
<template>
<div class="grandpa">
<h2>爷组件</h2>
<div>{{ msg }}</div>
<parent :message="message" :message2="message2" @sendGradpaMessage="getMessage"/>
</div>
</template>
<script>
import parent from './parent.vue';
export default {
name: 'grandpa',
data() {
return {
message: 'This is some shared data1',
message2: 'This is some shared data2',
msg:""
}
},
components: {
parent
},
methods:{
getMessage(msg){
console.log('msg::: ', msg);
this.msg = msg
},
}
}
</script>
// parent.vue
<template>
<div class="parent">
<h2>父组件</h2>
<div>{{ message }}</div>
<son v-bind="$attrs" v-on="$listeners"/>
</div>
</template>
<script>
import son from './son.vue';
export default {
name: 'parent',
data() {
return {
}
},
components: { son },
computed: {
message() {
// 从 $attrs 中删除已定义的 prop
console.log('this.$attrs::: ', this.$attrs);
const { message} = this.$attrs;
return message;
}
},
}
</script>
// son.vue
<template>
<div class="son">
<h2>孙组件</h2>
<div>{{ $attrs.message }}</div>
<div>{{ message }}</div>
<el-button type="primary" @click="sendMessage">发送消息</el-button>
</div>
</template>
<script>
export default {
name: 'son',
data() {
return {
}
},
computed: {
message() {
// 从 $attrs 中删除已定义的 prop
console.log('this.$attrs::: ', this.$attrs);
const { message2} = this.$attrs;
return message2;
}
},
methods: {
sendMessage(){
const msg = 'Hello from son component!';
this.$emit('sendGradpaMessage', msg)
}
}
}
</script>
- grandpa.vue 组件提供了 message, message2 两个参数。
- parent.vue 组件通过 $attrs 接收了 message 参数,并在模板中使用 message。
- son.vue 组件通过 $attrs 接收了 message2 参数,并在模板中使用 message,同时通过点击按钮使用 $emit方法将参数 msg 传给了 parent.vue 组件,parent.vue 通过 $listeners 将事件传递给grandpa.vue。
vue3.x 使用
在 Vue 3 中,$attrs 和 $listeners 的使用有一些变化,特别是在 $listeners 的处理上。Vue 3 已经不再单独提供 $listeners,而是将事件监听器和其他属性一起放在 $attrs 中。这意味着在 Vue 3 中,你需要通过 $attrs 来传递所有的属性和事件监听器。
// grandpa.vue
<template>
<div class="grandpa">
<h2>爷组件</h2>
<div>{{ receive }}</div>
<parent :message="message" :message2="message2" @sendGrandpaMessage="getMessage" />
</div>
</template>
<script setup lang="ts" name="grandpa">
import { ref } from 'vue';
import parent from './parent.vue';
// 定义一个可变的值
const message = ref('Hello from grandpa');
const message2 = ref('Hello from Parent');
// 定义一个接收消息
const receive = ref('111');
// 接收子组件传的参数
const getMessage = (msg: string) => {
console.log('msg::', msg);
receive.value = msg
};
</script>
// parent.vue
<template>
<div class="parent">
<h2>父组件</h2>
<div>{{ message2 }}</div>
<div>{{ $attrs }}</div>
<son v-bind:="$attrs" />
</div>
</template>
<script setup lang="ts" name="parent">
import son from './son.vue';
defineProps(['message2']);
</script>
// son.vue
<template>
<div class="son">
<h2>孙组件</h2>
<div>{{ message }}</div>
<el-button type="primary" @click="sendMessage()">发送消息</el-button>
</div>
</template>
<script setup lang="ts" name="son">
import { defineProps, defineEmits } from 'vue';
defineProps(['message',]);
const emits = defineEmits(['sendGrandpaMessage']);
// 处理事件
function sendMessage() {
const msg = 'Hello from child';
emits('sendGrandpaMessage', msg);
}
</script>
- grandpa.vue 组件提供了 message, message2 两个参数和 sendGrandpaMessage 方法
- parent.vue 组件通过 $attrs 接收了 message2 参数,并在模板中使用 message2。
- son.vue 组件通过 $attrs 接收了 message 参数,并在模板中使用 message,同时通过点击按钮使用 emit 将参数 msg 传给了parent.vue 组件,parent.vue 通过 $attrs 将事件传递给 grandpa.vue。
provide 与 inject
在祖先组件定义provide属性,返回传递的值
在后代组件通过inject接收组件传递过来的值