在 Vue 中,父子组件的通信是组件间数据传递的核心机制之一。以下是常见的几种通信方式及适用场景:
1. Props(父 → 子)
- 用途:父组件通过
props
向子组件传递数据,单向数据流。 - 语法:
- 父组件:通过
v-bind
或简写:
绑定属性。
<ChildComponent :message="parentMessage" />
- 子组件:声明
props
接收数据。
export default {props: ['message'] }
- 父组件:通过
- 示例:
<!-- Parent.vue --> <template><Child :text="helloFromParent" @click="handleClick" /> </template><script> import Child from './Child.vue'; export default {components: { Child },data() {return { helloFromParent: 'Hello!' };} }; </script><!-- Child.vue --> <template><div @click="sendMessageToParent">{{ text }}</div> </template><script> export default {props: ['text'],methods: {sendMessageToParent() {this.$emit('custom-event', 'Message from Child');}} }; </script>
2. Events(子 → 父)
-
用途:子组件通过自定义事件向父组件发送消息,触发父组件的方法。
-
语法:
- 子组件:使用
$emit
触发事件。
this.$emit('update', newValue);
- 父组件:监听事件并绑定处理方法。
<Child @update="handleUpdate" />
- 子组件:使用
-
示例:
// Child.vue 中触发事件 methods: {sendToParent() {this.$emit('child-event', 'Data from Child');} }
// Parent.vue 监听事件 <Child @child-event="parentMethod" />
3. Slot(插槽)
-
用途:父组件通过插槽向子组件传递内容(HTML 或组件),子组件定义插槽位置。
-
分类:
- 默认插槽:
<slot></slot>
- 具名插槽:
<slot name="header"></slot>
- 作用域插槽:允许父组件传递数据到插槽内容中。
- 默认插槽:
-
示例(作用域插槽):
<!-- Child.vue --> <template><div><h3>Child Component:</h3><slot name="content" :data="childData"></slot></div> </template><script> export default {data() {return { childData: 'This is from Child' };} }; </script>
<!-- Parent.vue 使用插槽 --> <Child><template #content="{ data }"><p>{{ data }}</p></template> </Child>
4. 自定义事件总线(非父子组件间通信)
- 用途:适用于任意组件间的通信(需注意 Vue 3 中已废弃
EventBus
,建议用mitt
或Vue.observable
替代)。 - 示例:
// 创建事件总线 const EventBus = Vue.observable({});// 组件 A 发送事件 EventBus.$emit('event-name', payload);// 组件 B 监听事件 EventBus.$on('event-name', (payload) => {console.log(payload); });
5. Provide / Inject(祖先 → 后代)
- 用途:跨层级组件通信,适用于祖父组件向孙组件传递数据。
- 语法:
- 父组件:通过
provide
提供数据。
provide() {return { sharedData: this.sharedData }; }
- 子组件:通过
inject
注入数据。
inject(['sharedData']);
- 父组件:通过
- 示例:
<!-- GrandParent.vue --> <template><Parent /> </template><script> export default {provide() {return { message: 'Hello from GrandParent' };} }; </script><!-- Child.vue --> <template>{{ injectedMessage }} </template><script> export default {inject: ['message'],computed: {injectedMessage() {return this.message;}} }; </script>
6. Ref 获取组件实例
- 用途:通过
ref
获取子组件实例,直接调用方法或访问数据(不推荐频繁使用)。 - 示例:
<!-- Parent.vue --> <template><Child ref="childRef" /><button @click="callChildMethod">Call Child Method</button> </template><script> import Child from './Child.vue'; export default {components: { Child },methods: {callChildMethod() {this.$refs.childRef.childMethod();}} }; </script>
总结
- 父子通信首选:
props
(父→子) +events
(子→父)。 - 内容分发:使用插槽(尤其是作用域插槽)。
- 复杂状态管理:考虑使用 Vuex 或 Pinia(集中式状态管理)。
- 跨层级通信:
provide/inject
或事件总线(根据项目规模选择)。
根据具体需求选择最适合的通信方式,保持代码的可维护性和可读性。