在 Vue3 中,组件通讯是一个非常重要的概念,它决定了如何在父子组件之间、兄弟组件之间以及跨层级组件之间传递数据和事件。本文将详细介绍 Vue3 中的各种组件通讯方式,并结合具体示例,全面展示各种场景下的实现方法。所有示例将使用 Vue3 的 setup 语法糖来书写。
一、父子组件通讯
父子组件通讯是 Vue 中最常见的一种通讯方式。主要通过 props 和 emits 来实现。
1. 父组件传递数据给子组件(Props)
父组件通过 props 向子组件传递数据,这是最基本的通讯方式。
父组件(Parent.vue):
<template>
<div>
<ChildComponent :message="parentMessage" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const parentMessage = ref('Hello from Parent!');
</script>
子组件(ChildComponent.vue):
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
message: {
type: String,
required: true,
},
});
</script>
2. 子组件向父组件传递事件(Emits)
子组件通过 emits 向父组件传递事件。
父组件(Parent.vue):
<template>
<div>
<ChildComponent @update="handleUpdate" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const handleUpdate = (message) => {
console.log('Received from child:', message);
};
</script>
子组件(ChildComponent.vue):
<template>
<button @click="sendUpdate">Click Me</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['update']);
const sendUpdate = () => {
emit('update', 'Hello from Child!');
};
</script>
二、兄弟组件通讯
兄弟组件之间的通讯通常需要借助于父组件作为中介。
父组件(Parent.vue):
<template>
<div>
<SiblingOne @message="handleMessage" />
<SiblingTwo :sharedMessage="sharedMessage" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import SiblingOne from './SiblingOne.vue';
import SiblingTwo from './SiblingTwo.vue';
const sharedMessage = ref('');
const handleMessage = (message) => {
sharedMessage.value = message;
};
</script>
兄弟组件一(SiblingOne.vue):
<template>
<button @click="sendMessage">Send Message</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['message']);
const sendMessage = () => {
emit('message', 'Hello from Sibling One!');
};
</script>
兄弟组件二(SiblingTwo.vue):
<template>
<div>{{ sharedMessage }}</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
sharedMessage: {
type: String,
default: '',
},
});
</script>
三、跨层级组件通讯
跨层级组件通讯可以通过 Provide 和 Inject 来实现,这对于避免深层组件传递 props 和事件非常有用。
祖先组件(Ancestor.vue):
<template>
<div>
<DescendantComponent />
</div>
</template>
<script setup>
import { provide, ref } from 'vue';
import DescendantComponent from './DescendantComponent.vue';
const sharedState = ref('Hello from Ancestor!');
provide('sharedState', sharedState);
</script>
后代组件(DescendantComponent.vue):
<template>
<div>{{ sharedState }}</div>
</template>
<script setup>
import { inject } from 'vue';
const sharedState = inject('sharedState');
</script>
四、通过 ref 传递组件实例
有时我们需要在父组件中访问子组件的方法或属性,可以使用 ref 来获取子组件实例。
父组件(Parent.vue):
<template>
<div>
<ChildComponent ref="child" />
<button @click="callChildMethod">Call Child Method</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const child = ref(null);
const callChildMethod = () => {
if (child.value) {
child.value.someMethod();
}
};
</script>
子组件(ChildComponent.vue):
<template>
<div>Child Component</div>
</template>
<script setup>
import { defineExpose } from 'vue';
const someMethod = () => {
console.log('Child method called!');
};
defineExpose({ someMethod });
</script>
五、使用 EventBus 实现全局事件通讯
在 Vue3 中,虽然官方不推荐使用 EventBus,但它仍然是一个简单的全局事件通讯方式。我们可以创建一个简单的 EventBus 实现:
EventBus.js:
import mitt from 'mitt';
const EventBus = mitt();
export default EventBus;
组件 A(ComponentA.vue):
<template>
<button @click="sendMessage">Send Global Message</button>
</template>
<script setup>
import EventBus from './EventBus';
const sendMessage = () => {
EventBus.emit('globalMessage', 'Hello from Component A!');
};
</script>
组件 B(ComponentB.vue):
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import EventBus from './EventBus';
const message = ref('');
onMounted(() => {
EventBus.on('globalMessage', (msg) => {
message.value = msg;
});
});
onUnmounted(() => {
EventBus.off('globalMessage');
});
</script>
六、使用 Vuex 实现全局状态管理
Vuex 是 Vue 官方提供的状态管理工具,可以帮助我们管理应用中的全局状态。下面是一个简单的示例。
安装 Vuex:
npm install vuex@next
Store(store.js):
import { createStore } from 'vuex';
export const store = createStore({
state() {
return {
message: 'Hello from Vuex!'
};
},
mutations: {
updateMessage(state, newMessage) {
state.message = newMessage;
}
},
actions: {
setMessage({ commit }, message) {
commit('updateMessage', message);
}
},
getters: {
message: (state) => state.message
}
});
主应用(main.js):
import { createApp } from 'vue';
import App from './App.vue';
import { store } from './store';
createApp(App)
.use(store)
.mount('#app');
组件 A(ComponentA.vue):
<template>
<button @click="updateMessage">Update Vuex Message</button>
</template>
<script setup>
import { useStore } from 'vuex';
const store = useStore();
const updateMessage = () => {
store.dispatch('setMessage', 'Hello from Component A!');
};
</script>
组件 B(ComponentB.vue):
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const message = computed(() => store.getters.message);
</script>
七、使用 Pinia 实现全局状态管理
Pinia 是 Vue3 的新一代状态管理库,比 Vuex 更轻量且更易用。下面是一个简单的示例。
安装 Pinia:
npm install pinia
Store(store.js):
import { defineStore } from 'pinia';
export const useMainStore = defineStore('main', {
state: () => ({
message: 'Hello from Pinia!'
}),
actions: {
setMessage(newMessage) {
this.message = newMessage;
}
}
});
主应用(main.js):
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount('#app');
组件 A(ComponentA.vue):
<template>
<button @click="updateMessage">Update Pinia Message</button>
</template>
<script setup>
import { useMainStore } from './store';
const store = useMainStore();
const updateMessage = () => {
store.setMessage('Hello from Component A!');
};
</
script>
组件 B(ComponentB.vue):
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { computed } from 'vue';
import { useMainStore } from './store';
const store = useMainStore();
const message = computed(() => store.message);
</script>
结语
在 Vue3 中,组件通讯有多种方式可供选择,每种方式都有其特定的应用场景。通过合理使用 props、emits、Provide/Inject、ref、EventBus、Vuex 以及 Pinia 等技术,可以实现灵活高效的组件通讯。希望本文的内容和示例能帮助你更好地理解和应用 Vue3 的组件通讯技术。