什么是组件通信?
组件通信指的是在 Vue 应用中,组件之间如何交换数据和事件。组件通信可以分为不同的场景,例如父子组件通信、兄弟组件通信、跨层级组件通信等。有效的组件通信能够使得应用的各个部分协同工作,提升开发效率和代码的可维护性。
在Vue 3中,组件间通信的方法主要有以下几种:
-
Props / Events: 父子组件通信
-
Provide / Inject: 父子孙组件通信
-
Global Properties: 全局属性
-
Event Bus:事件总线
-
使用Vuex或Pinia进行状态管理
1.Props / Events: 父子组件通信
1.1 Props:
用于父组件向子组件传递数据
1.2 Emit:
用于子组件向父组件发送事件。
父组件
<template>
<ChildComponent :message="parentMessage" />
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const parentMessage = ref('Hello from Parent!');
</script>
子组件
<template>
<p>{{ message }}</p>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
message: String
});
</script>
2. Provide / Inject: 父子孙组件通信
provide 和 inject 是用于在组件树中跨层级传递数据的API,它们特别适用于祖先组件和后代组件(不一定是直接的父子关系)之间的通信。这种方式允许你在组件树的深处访问在高层级组件中定义的数据或方法,而不需要通过每一层级的组件逐层传递。
1.在祖先组件中使用 provide
provide 可以用来提供一个值,这个值可以是任何类型的数据或方法。它通常在祖先组件的 setup 函数中调用。
<!-- AncestorComponent.vue -->
<template>
<ChildComponent />
</template>
<script setup>
import { provide, ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const sharedData = ref('This is shared data');
// 提供 sharedData 给后代组件
provide('sharedDataKey', sharedData);
</script>
2. 在后代组件中使用 inject
后代组件可以使用 inject 来接收祖先组件通过 provide 提供的数据或方法。inject 也通常在组件的 setup 函数中调用。
<!-- ChildComponent.vue -->
<template>
<div>
<p>接收到的数据: {{ sharedData }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue';
// 从祖先组件中注入 sharedData
const sharedData = inject('sharedDataKey');
</script>
3.Global Properties
全局配置:使用app.config.globalProperties来定义可以在所有组件实例中访问的全局属性
// main.js
const app = Vue.createApp({...});
app.config.globalProperties.$myProperty = 'Hello from global property';
这样,在任何组件中都可以通过this.$myGlobalProperty来访问这个全局属性。
- 全局混入:使用
app.mixin将可复用的功能混入到所有组件中。这可以用于添加全局的生命周期钩子、方法等。 - 全局指令:使用
app.directive注册可以在整个应用程序中使用的自定义指令。虽然这不是直接用于传递数据,但它可以作为一种全局的功能扩展。 - 全局组件:使用
app.component注册可以在整个应用程序中使用的全局组件。这有助于在多个地方重用相同的组件,但同样不是直接用于数据传递。
全局属性的使用
一旦设置了全局属性,就可以在组件中通过this关键字来访问它们。例如,对于上面设置的全局属性$myGlobalProperty,可以在任何组件的方法中通过this.$myGlobalProperty来获取其值。
此外,在Vue3的Composition API中,可以通过getCurrentInstance函数获取当前组件的实例,并通过该实例的proxy属性来访问全局属性
import { getCurrentInstance } from 'vue';
const instance = getCurrentInstance();
console.log(instance.proxy.$myGlobalProperty);
4. Event Bus
1.创建一个事件总线,用于组件间发送和监听事件。
// event-bus.js
import Vue from 'vue';
export const EventBus = new Vue();
2. 发送事件的组件
// 发送事件的组件
<script setup>
import { EventBus } from './event-bus.js';
EventBus.$emit('message', 'Hello from event bus');
</script>
3. 监听事件的组件
// 监听事件的组件
<script setup>
import { onMounted } from 'vue';
import { EventBus } from './event-bus.js';
onMounted(() => {
EventBus.$on('message', handleMessage);
});
function handleMessage(msg) {
console.log(msg);
}
</script>
5. Vuex或Pinia进行状态管理
对于更复杂的场景,尤其是跨组件层级或兄弟组件间的大量数据共享,建议使用Vuex或Pinia这样的状态管理库。这里仅展示Pinia的基本用法:
1. 安装Pinia
npm install pinia
2. 创建Pinia Store
// store/index.js
import { createPinia } from 'pinia';
const pinia = createPinia();
export default pinia;
3. 在main.js中挂载Pinia
import { createApp } from 'vue';
import App from './App.vue';
import pinia from './store';
const app = createApp(App);
app.use(pinia);
app.mount('#app');
上面这两步操作可以可以写在 main.js中 可个人习惯
4. 定义一个Store
// store/useMyStore.js
import { defineStore } from 'pinia';
export const useMyStore = defineStore('myStore', {
state: () => ({
sharedMessage: 'This is a shared message'
}),
actions: {
updateMessage(newMessage) {
this.sharedMessage = newMessage;
}
}
});
5.在组件中使用Store
<template>
<p>{{ message }}</p>
<button @click="updateMessage">Update Message</button>
</template>
<script setup>
import { useMyStore } from '../store/useMyStore';
const store = useMyStore();
const message = store.sharedMessage;
const updateMessage = () => {
store.updateMessage('Updated Message!');
};
</script>