在Vue中,组件之间的通信可以有多种方式实现:
-
Props 和 $emit
- Props:父组件向子组件传递数据时,通过属性绑定(
v-bind
或:
)将数据作为属性传给子组件。子组件需要在props选项中声明它接收的属性列表。 - **emit∗∗:子组件向父组件发送事件和数据,通过‘emit**:子组件向父组件发送事件和数据,通过 `emit∗∗:子组件向父组件发送事件和数据,通过‘emit(eventName, payload)
触发自定义事件,并附带数据。父组件可以在使用子组件的地方监听这个事件,通常通过
v-on: eventName或
@eventName` 来实现。
- Props:父组件向子组件传递数据时,通过属性绑定(
-
refs
- refs:直接访问子组件实例或DOM元素。当需要获取子组件的状态或者调用其方法时,可以给子组件添加一个ref引用,在父组件中通过‘this.refs**:直接访问子组件实例或DOM元素。当需要获取子组件的状态或者调用其方法时,可以给子组件添加一个ref引用,在父组件中通过 `this.refs∗∗:直接访问子组件实例或DOM元素。当需要获取子组件的状态或者调用其方法时,可以给子组件添加一个ref引用,在父组件中通过‘this.refs.refName` 访问该子组件实例。
-
Event Bus (全局事件总线)
- 创建一个独立的 Vue 实例作为事件中心,通过它的
$on
和$emit
方法来在任何组件之间发送和接收事件,以此实现跨级或兄弟组件间的通信。
- 创建一个独立的 Vue 实例作为事件中心,通过它的
-
Vuex
- Vuex 是 Vue 提供的状态管理库,用于管理整个应用层级的状态,允许组件间共享状态并通过 actions、mutations 进行状态的变更和同步。
-
依赖注入(provide / inject)
- 父组件可以通过
provide
选项提供数据,而无需显式传递 props,然后在子孙组件中通过inject
选项来注入并使用这些数据。
- 父组件可以通过
-
父子组件通信补充方式
- 使用回调函数作为 Prop,父组件传递一个方法给子组件,子组件在适当的时候调用此方法以更新父组件的状态。
- 使用
v-model
进行双向数据绑定(仅限于表单输入类组件或其他支持 v-model 的自定义组件)。
-
动态组件与 keep-alive 缓存
- 动态组件配合
keep-alive
可以在切换组件时保留状态,并且可以通过激活/停用状态的改变进行间接通信。
- 动态组件配合
Props 和 $emit 父组件
父组件
<template>
<div>
<child-component :message="parentMessage" @update-message="handleUpdateMessage"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: { ChildComponent },
data() {
return {
parentMessage: 'Hello from parent'
}
},
methods: {
handleUpdateMessage(newMessage) {
this.parentMessage = newMessage;
}
}
}
</script>
子组件
<template>
<div>
{{ message }}
<button @click="updateMessage">Update Message</button>
</div>
</template>
<script>
export default {
props: ['message'],
methods: {
updateMessage() {
this.$emit('update-message', 'New message from child');
}
}
}
</script>
refs
父组件
<template>
<div>
<child-component ref="childRef"></child-component>
<button @click="callChildMethod">Call Child Method</button>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: { ChildComponent },
methods: {
callChildMethod() {
this.$refs.childRef.childMethod();
}
}
}
子组件
<script>
export default {
methods: {
childMethod() {
console.log('Child method called');
}
}
}
</script>
Event Bus
// eventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
// 在任意组件内发送事件
EventBus.$emit('customEvent', payload);
// 在任意组件内接收事件
EventBus.$on('customEvent', (payload) => {
console.log(payload);
});
Vuex
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
sharedData: 'Global State'
},
mutations: {
updateSharedData(state, newData) {
state.sharedData = newData;
}
},
actions: {
updateData({ commit }, newData) {
commit('updateSharedData', newData);
}
},
getters: {
getSharedData(state) {
return state.sharedData;
}
}
});
组件中使用
<script>
import { mapState, mapActions } from 'vuex';
export default {
computed: {
...mapState(['sharedData'])
},
methods: {
...mapActions(['updateData'])
},
mounted() {
this.updateData('Updated Global State');
console.log(this.sharedData);
}
};
</script>
provide / inject
父组件
<script>
provide() {
return {
parentData: 'Provided by Parent'
};
}
</script>
子组件
<script>
inject: ['parentData'],
created() {
console.log(this.parentData); // "Provided by Parent"
}
</script>
子向父传递
父组件
<template>
<div>
<child-component :notifyParent="updateParentData"></child-component>
<p>Parent Data: {{ parentData }}</p>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: { ChildComponent },
data() {
return {
parentData: ''
}
},
methods: {
updateParentData(newData) {
this.parentData = newData;
}
}
}
</script>
子组件
<template>
<button @click="callParentMethod">Update Parent Data</button>
</template>
<script>
export default {
props: {
notifyParent: { type: Function, required: true }
},
methods: {
callParentMethod() {
this.notifyParent('New data from child');
}
}
}
</script>
v-model 进行双向数据绑定
父组件
<template>
<input-typeahead v-model="searchText" />
<p>Search Text: {{ searchText }}</p>
</template>
<script>
import InputTypeahead from './InputTypeahead.vue'
export default {
components: { InputTypeahead },
data() {
return {
searchText: ''
}
}
}
</script>
子组件
<template>
<input type="text" :value="value" @input="onInput" />
</template>
<script>
export default {
model: {
prop: 'value',
event: 'input'
},
props: {
value: String
},
methods: {
onInput(event) {
this.$emit('input', event.target.value);
}
}
}
</script>
动态组件与 keep-alive 缓存进行间接通信
<template>
<div>
<button @click="currentTab = 'tab1'">Tab 1</button>
<button @click="currentTab = 'tab2'">Tab 2</button>
<keep-alive>
<component :is="currentTab === 'tab1' ? Tab1 : Tab2" @change-data="handleDataChange" />
</keep-alive>
</div>
</template>
<script>
import Tab1 from './Tab1.vue';
import Tab2 from './Tab2.vue';
export default {
components: { Tab1, Tab2 },
data() {
return {
currentTab: 'tab1',
sharedData: {}
}
},
methods: {
handleDataChange(data) {
this.sharedData = data;
}
}
}
</script>
其中,Tab1 和 Tab2 组件可以通过 $emit
触发 change-data
事件来更新父组件的状态。当切换回已缓存的组件时,由于状态被 keep-alive
保留,可以继续显示上次离开时的数据或状态。