父向子通信
1.定义props
子组件中,定义期望接收的属性。例如,在子组件的
script
部分:
export default { props: { message: String // 假设父组件要传递一个字符串类型的数据 } }
复制
2.传递数据
在父组件的模板中,通过属性绑定的方式将数据传递给子组件。
<template> <ChildComponent :message="parentMessage" /> </template>
复制
这里
parentMessage
是父组件的数据属性,:message
是将该数据绑定到子组件的message
prop上。
子向父通信
1.触发事件
在子组件中,当需要通知父组件时,可以使用
this.$emit
触发一个自定义事件,并可附带参数。
methods: { sendDataToParent() { this.$emit('child-event', { someData: 'Hello from child!' }); } }
复制
2.监听事件
在父组件的模板中,使用
v-on
或简写@
来监听子组件触发的事件,并定义处理函数。
<template> <ChildComponent @child-event="handleChildEvent" /> </template> <script> export default { methods: { handleChildEvent(childData) { console.log('Received data from child:', childData); } } } </script>
复制
当子组件触发
child-event
时,父组件的handleChildEvent
方法会被调用,并接收到子组件传递的数据
通过v-model简化代码
通过
v-model
,父组件可以直接绑定一个变量到子组件的输入值,而不需要显式地定义事件监听器或处理函数来更新数据。子组件通过监听内部的变动并发出input
事件,维持了这种双向绑定关系.
//父组件 <template> <div> <base-select v-model="selectedCity"></base-select> </div> </template> <script> import BaseSelect from './BaseSelect.vue'; export default { components: { BaseSelect }, data() { return { selectedCity: '' // 这个变量将与BaseSelect组件的选择值双向绑定 }; } }; </script>
复制
//子组件 <template> <select :value="value" @change="handleChange"> <!-- 选项列表 --> </select> </template> <script> export default { props: { value: String // 用来接收v-model绑定的值 }, methods: { handleChange(e) { this.$emit('input', e.target.value); // 当下拉选项变化时,触发input事件并传递新值 } } }; </script>
复制
注意
- 单向数据流: Vue推荐数据流动主要遵循单向数据流原则,即数据从父组件流向子组件,通过事件回调进行反向通信,保持数据流向的清晰。
- 避免直接修改Props: 子组件不应该直接修改接收到的props,如果需要修改数据,应该通过触发事件让父组件去处理。如果确实需要局部状态,可以使用子组件的本地数据。
事件总线
1.创建Event Bus实例
创建一个Vue实例作为事件总线。这个实例并不用于渲染任何内容,而是纯粹作为事件的中心枢纽
import Vue from 'vue'; export const EventBus = new Vue();
复制
2.发送事件(发布)
在发送事件的组件中,使用
EventBus.$emit
方法触发一个事件,并可以传递数据。
// 在某个组件中 import { EventBus } from './eventBus.js'; methods: { sendMessage() { EventBus.$emit('messageSent', { text: 'Hello from sender!' }); } }
复制
3.监听事件(订阅)
需要接收事件的组件中,使用
EventBus.$on
方法监听特定事件。
// 在另一个组件中 import { EventBus } from './eventBus.js'; mounted() { EventBus.$on('messageSent', this.receiveMessage); }, beforeDestroy() { // 为了避免内存泄漏,记得在组件销毁前移除事件监听器 EventBus.$off('messageSent', this.receiveMessage); }, methods: { receiveMessage(payload) { console.log('Received message:', payload.text); } }
复制
注意
- 内存泄漏:由于事件监听器可能长期存在,务必在组件卸载时使用
EventBus.$off
移除不再需要的监听器,以防止内存泄漏。- 代码可维护性:随着应用复杂度增加,过度依赖Event Bus可能导致代码难以理解和维护。对于大型应用,可能需要转向使用Vuex这样的状态管理库来更系统地管理状态和事件。
- 替代方案:Vue 3引入了Composition API,可以通过创建自定义组合API或使用第三方库如
pinia
来实现更灵活的状态管理和跨组件通信,减少对Event Bus的依赖。
祖先向后代通信
1.在祖先组件中使用 provide
祖先组件中使用
provide
来提供数据或方法。provide
接收一个对象或返回对象的函数,这个对象的属性将被注入到后代组件中。
// AncestorComponent.vue export default { provide() { return { message: 'Hello from ancestor', someMethod() { console.log('This method is provided by ancestor.'); } }; } };
复制
2. 在后代组件中使用 inject
后代组件使用
inject
来接收这些提供的数据或方法。可以直接注入具体的键名,或者提供一个对象来配置注入行为。
// DescendantComponent.vue export default { inject: ['message', 'someMethod'], created() { console.log(this.message); // 输出: Hello from ancestor this.someMethod(); // 输出: This method is provided by ancestor. } };
复制
或者使用对象形式进行更详细的配置:
export default { inject: { message: { from: 'message' }, // "from" 指定了要注入的属性名 anotherNameForSomeMethod: { from: 'someMethod' } // 甚至可以重命名注入的属性 }, created() { console.log(this.message); this.anotherNameForSomeMethod(); } };
复制
注意
- 层级限制:
provide
和inject
只能在组件树的后代中生效,不能跳过中间层获取数据。- 适用场景:这种方式适用于那些深层次嵌套或不确定层次关系的组件间通信,比如主题切换、全局配置等。
- 替代方案:对于复杂的应用,考虑使用 Vuex 状态管理模式,它能更高效、清晰地管理全局状态。