在 Vue 3 中,组合式 API (setup()) 提供了一种更灵活的方式来组织和重用逻辑。使用组合式 API,我们可以更好地分离关注点,并利用响应式引用和生命周期钩子等功能。下面是使用组合式 API 实现组件间通信的一些最佳实践及其代码示例。
1. Props 和 Emit
这是最基本的通信方式,适用于父子组件之间的通信。
示例
父组件 (Parent.vue)
<template>
<Child :message="message" @updateMessage="handleUpdateMessage" />
<button @click="changeMessage">Change Message</button>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const message = ref('Hello from Parent');
function handleUpdateMessage(newMessage) {
message.value = newMessage;
}
function changeMessage() {
message.value = 'Changed by Parent';
}
</script>
子组件 (Child.vue)
<template>
<div>{{ message }}</div>
<button @click="updateParent">Update Parent</button>
</template>
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
message: String,
});
const emit = defineEmits(['updateMessage']);
function updateParent() {
emit('updateMessage', 'Hello from Child');
}
watch(() => props.message, (newVal) => {
console.log('Message changed:', newVal);
});
</script>
2. Refs 和 Custom Events
在 Vue 3 中,你可以使用 refs 来引用子组件,并通过 $refs 访问子组件实例的方法。
示例
父组件 (Parent.vue)
<template>
<Child ref="childRef" />
<button @click="callChildMethod">Call Child Method</button>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const childRef = ref(null);
function callChildMethod() {
childRef.value.childMethod();
}
</script>
子组件 (Child.vue)
<template>
<button @click="emitEvent">Emit Event</button>
</template>
<script setup>
import { onMounted } from 'vue';
function emitEvent() {
console.log('Event emitted');
}
function childMethod() {
console.log('Called from parent');
}
onMounted(() => {
console.log('Child component mounted');
});
</script>
3. Composition API 中的 Context
Composition API 提供了 provide/inject 上下文,可以在祖先组件中提供数据,在子孙组件中注入数据。
示例
祖先组件 (Ancestor.vue)
<template>
<GrandChild />
</template>
<script setup>
import { provide } from 'vue';
import GrandChild from './GrandChild.vue';
provide('message', 'Hello from Ancestor');
</script>
子孙组件 (GrandChild.vue)
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { inject } from 'vue';
const message = inject('message');
</script>
4. 使用 Vuex
对于更复杂的通信场景,可以使用 Vuex 状态管理库。
示例
store/index.js
import { createStore } from 'vuex';
export default createStore({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
},
actions: {
increment({ commit }) {
commit('increment');
},
},
getters: {
doubleCount: (state) => state.count * 2,
},
});
组件 (Counter.vue)
<template>
<button @click="increment">Increment</button>
<div>{{ doubleCount }}</div>
</template>
<script setup>
import { mapActions, mapGetters } from 'vuex';
import { useStore } from 'vuex';
const store = useStore();
function increment() {
store.dispatch('increment');
}
const doubleCount = store.getters.doubleCount;
</script>
5. 使用 Pinia
Pinia 是 Vue 3 的官方推荐的状态管理库,提供了与 Vuex 类似的功能,但更加简洁易用。
示例
store/index.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++;
},
},
getters: {
doubleCount: (state) => state.count * 2,
},
});
组件 (Counter.vue)
<template>
<button @click="increment">Increment</button>
<div>{{ doubleCount }}</div>
</template>
<script setup>
import { useCounterStore } from './store/index';
const counterStore = useCounterStore();
function increment() {
counterStore.increment();
}
const doubleCount = counterStore.doubleCount;
</script>
6. 使用 Event Bus
尽管 Event Bus 不再作为 Vue 3 的官方推荐方法,但它仍然是一种可行的选择,特别是对于简单的跨组件通信。
示例
event-bus.js
import { createApp } from 'vue';
const app = createApp({});
export const eventBus = app.config.globalProperties.$bus;
发布者组件 (Publisher.vue)
<template>
<button @click="publish">Publish</button>
</template>
<script setup>
import { eventBus } from './event-bus';
function publish() {
eventBus.emit('my-event', 'Hello from Publisher');
}
</script>
订阅者组件 (Subscriber.vue)
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { eventBus } from './event-bus';
const message = ref('');
eventBus.on('my-event', (msg) => {
message.value = msg;
});
onUnmounted(() => {
eventBus.off('my-event');
});
</script>
7. 使用 MITT方式
MITT 是一个轻量级的事件总线库,特别适合用于 Vue 3 的组合式 API 中的组件间通信。MITT 的设计非常简单,易于集成,并且不引入额外的复杂性。下面是如何使用 MITT 进行组件间通信的详细示例。
安装 MITT
首先,你需要安装 MITT。你可以通过 npm 或 yarn 来安装:
npm install mitt
# 或者
yarn add mitt
创建全局事件总线
在 Vue 3 项目中,你可以在 main.js 或 main.ts 文件中创建一个全局的事件总线实例。
main.js
import { createApp } from 'vue';
import App from './App.vue';
import mitt from 'mitt';
const emitter = mitt();
const app = createApp(App);
app.provide('$emitter', emitter);
app.mount('#app');
这里我们创建了一个名为 $emitter 的全局事件总线,并通过 provide() 方法使其在整个应用程序中可用。
使用 MITT 进行通信
示例:父组件向子组件发送事件
父组件 (Parent.vue)
<template>
<Child />
<button @click="sendEvent">Send Event</button>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
import { inject } from 'vue';
const $emitter = inject('$emitter');
function sendEvent() {
$emitter.emit('custom-event', 'Hello from Parent');
}
</script>
子组件 (Child.vue)
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { inject } from 'vue';
const message = ref('');
const $emitter = inject('$emitter');
onMounted(() => {
$emitter.on('custom-event', (msg) => {
message.value = msg;
});
});
onUnmounted(() => {
$emitter.off('custom-event');
});
</script>
在这个例子中,父组件通过 $emitter 发送了一个名为 custom-event 的事件,子组件则监听这个事件并在接收到事件时更新其状态。
示例:多个组件之间的通信
组件 A (ComponentA.vue)
<template>
<button @click="sendEvent">Send Event</button>
</template>
<script setup>
import { onMounted } from 'vue';
import { inject } from 'vue';
const $emitter = inject('$emitter');
function sendEvent() {
$emitter.emit('global-event', 'Hello from Component A');
}
</script>
组件 B (ComponentB.vue)
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { inject } from 'vue';
const message = ref('');
const $emitter = inject('$emitter');
onMounted(() => {
$emitter.on('global-event', (msg) => {
message.value = msg;
});
});
onUnmounted(() => {
$emitter.off('global-event');
});
</script>
在这个例子中,组件 A 发送了一个名为 global-event 的事件,而组件 B 则监听这个事件并在接收到事件时更新其状态。
总结
这些示例展示了如何使用 Vue 3 的组合式 API (setup()) 实现组件间通信的不同方式。选择哪种方法取决于你的具体需求。对于简单的父子组件通信,使用 Props 和 Emit 就足够了。对于更复杂的场景,可以考虑使用 Vuex 或 Pinia。如果你只需要在有限的几个组件之间共享状态,Event Bus 或 Composition API 中的 Context 也是不错的选择。使用MIITT,MITT 是一种轻量级且易于使用的组件间通信方式,非常适合用于 Vue 3 的组合式 API。通过使用全局事件总线,你可以轻松地在组件之间发送和接收事件。这种方式特别适用于需要跨组件进行通信的场景,同时保持代码的简洁性和可维护性。