组件通讯方式
约 619 字大约 2 分钟
2026-03-11
思考
组件间通讯方式有哪些?
Props/$emit
父组件通过向子组件传递 props ,子组件通过 emit 触发事件来实现父子组件的通讯。
Parent.vue
<template>
<Child
:title="parentTitle"
@update:title="handleUpdate"
/>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const parentTitle = ref('初始标题');
const handleUpdate = (newTitle) => {
parentTitle.value = newTitle;
};
</script>Child.vue
<template>
<div>
<h3>{{ title }}</h3>
<button @click="changeTitle">修改标题</button>
</div>
</template>
<script setup>
// 定义 Props
const props = defineProps({
title: String
});
// 定义 emits
const emit = defineEmits(['update:title']);
const changeTitle = () => {
emit('update:title', '新标题');
};
</script>在只需要一级通讯时(父子组件通讯),这种方式数据流向清晰,最常用。但需要多层嵌套时,透传属性则较繁琐。
Provide/Inject
用于跨层级通讯,避免层层传递 Props。父组件提供(provide)数据,后代组件注入(inject)数据。
Ancestor.vue
<script setup>
import { provide, ref } from 'vue';
const theme = ref('dark');
// 提供数据(可以是响应式)
provide('theme', theme);
provide('updateTheme', (newTheme) => {
theme.value = newTheme;
});
</script>Descendant.vue
<script setup>
import { inject } from 'vue';
// 注入数据
const theme = inject('theme');
const updateTheme = inject('updateTheme');
</script>使用 provide/inject 解决了 props 透传问题。
refs/parent
通过 refs 获取子组件实例,然后调用实例上的方法。
Parent.vue
<template>
<Child ref="childRef" />
<button @click="callChildMethod">调用子方法</button>
</template>
<script setup>
import { ref } from 'vue';
const childRef = ref(null);
const callChildMethod = () => {
childRef.value.doSomething(); // 直接调用子组件方法
};
</script>Child.vue
<script setup>
const doSomething = () => {
console.log('子组件方法被调用');
};
defineExpose({ doSomething }); // Vue 3 必须暴露才能被访问
</script>耦合度高,难以测试,不建议用于数据传递。
事件总线
通过事件总线(Event Bus)实现组件间通讯。Vue 3 移除了 $on/$off/$emit,需使用第三方库(如 mitt)。
npm install mitt引入三方库后,创建一个事件总线实例:
import mitt from 'mitt';
export const emitter = mitt();ComponentA.vue
<script setup>
import { emitter } from './bus';
// 发送数据
const sendData = () => {
emitter.emit('custom-event', { msg: 'Hello' });
};
</script>ComponentB.vue
<script setup>
import { emitter } from './bus';
import { onMounted, onUnmounted } from 'vue';
onMounted(() => {
emitter.on('custom-event', (data) => {
console.log(data);
});
});
onUnmounted(() => {
emitter.off('custom-event'); // 必须清理,防止内存泄漏
});
</script>使用这种方式,可以实现任意组件之间通信。但如果过多使用,会造成数据流向混乱,难以维护,容易内存泄漏。
全局状态管理 Pinia/Vuex
全局状态管理适用于任意组件共享状态,尤其是复杂应用。若组件间通信需求大,可考虑使用。