Vue3.js組件通信,兄弟組件、父子、祖孫組件間通信
在 Vue.js 3 中,組件通信主要包括父子組件通信、兄弟組件通信以及祖孫組件通信。以下是各種情況下的常見通信方式:
1. 父子組件通信:
1.1 Props 傳遞數(shù)據(jù):
父組件通過 props 向子組件傳遞數(shù)據(jù):
<!-- Parent.vue -->
<template>
<Child :message="parentMessage" />
</template>
<script>
import Child from './Child.vue';
export default {
data() {
return {
parentMessage: 'Hello from Parent!'
};
},
components: {
Child
}
};
</script>
<!-- Child.vue -->
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: ['message']
};
</script>
1.2 事件監(jiān)聽子組件:
子組件通過 $emit 觸發(fā)事件,父組件通過監(jiān)聽這些事件來響應(yīng):
<!-- Parent.vue -->
<template>
<Child @notifyParent="handleNotify" />
</template>
<script>
import Child from './Child.vue';
export default {
methods: {
handleNotify(message) {
console.log(message); // 處理從子組件傳遞的數(shù)據(jù)
}
},
components: {
Child
}
};
</script>
<!-- Child.vue -->
<template>
<button @click="notifyParent">Notify Parent</button>
</template>
<script>
export default {
methods: {
notifyParent() {
this.$emit('notifyParent', 'Hello from Child!');
}
}
};
</script>
1.3 definEexpose暴露子組件方法
在 Vue 3 中,使用 TypeScript 編寫組件時,如果想要將子組件的方法暴露給父組件,可以使用 defineExpose 函數(shù)。defineExpose 允許你定義哪些內(nèi)容應(yīng)該被暴露給父組件,以供父組件訪問。
下面是一個簡單的例子,演示如何在子組件中使用 defineExpose 暴露方法:
// ChildComponent.vue
<template>
<div>
<button @click="incrementCounter">Increment Counter</button>
</div>
</template>
<script setup>
import { ref, defineExpose } from 'vue';
const counter = ref(0);
const incrementCounter = () => {
counter.value++;
};
// 暴露方法給父組件
defineExpose({
incrementCounter
});
</script>
在這個例子中,incrementCounter 方法被定義在子組件中,并通過 defineExpose 函數(shù)進行了暴露。父組件可以通過子組件的引用來調(diào)用這個方法。
// ParentComponent.vue
<template>
<div>
<ChildComponent ref="childRef" />
<button @click="callChildMethod">Call Child Method</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const childRef = ref();
const callChildMethod = () => {
// 調(diào)用子組件的方法
childRef.value.incrementCounter();
};
</script>
在父組件中,通過 ref 引用子組件,然后可以調(diào)用子組件中暴露的方法。
這樣,通過 defineExpose 可以在 Vue 3 中很方便地實現(xiàn)子組件方法的暴露。這對于構(gòu)建可重用組件庫或復(fù)雜組件通信場景非常有用。
$listeners批量綁定子組件事件
$listeners 是一個對象,包含了父組件傳遞給子組件的所有事件監(jiān)聽器。這個對象允許子組件將所有未被自身處理的事件監(jiān)聽器綁定到合適的 HTML 元素上。
通常情況下,當(dāng)你在子組件中使用 v-on="$listeners" 時,它會將所有的父組件傳遞下來的事件監(jiān)聽器應(yīng)用到相應(yīng)的 DOM 元素上。
以下是一個簡單的例子,演示了 $listeners 的使用:
<!-- ParentComponent.vue -->
<template>
<ChildComponent @custom-event="handleCustomEvent" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
methods: {
handleCustomEvent(data) {
console.log('Custom Event Handled in Parent:', data);
}
},
components: {
ChildComponent
}
};
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<button @click="triggerCustomEvent">Trigger Custom Event</button>
</div>
</template>
<script>
export default {
methods: {
triggerCustomEvent() {
// 觸發(fā)自定義事件并傳遞數(shù)據(jù)
this.$emit('custom-event', 'Hello from Child');
}
}
};
</script>
在子組件中,當(dāng)用戶點擊按鈕時,觸發(fā)了一個自定義事件,并通過 $emit 傳遞了數(shù)據(jù)給父組件。如果你希望在父組件中使用該事件監(jiān)聽器,可以通過 $listeners 將其傳遞到子組件的相應(yīng)元素上:
<!-- ChildComponent.vue -->
<template>
<div>
<button @click="triggerCustomEvent" v-on="$listeners">Trigger Custom Event</button>
</div>
</template>
<script>
export default {
methods: {
triggerCustomEvent() {
// 觸發(fā)自定義事件并傳遞數(shù)據(jù)
this.$emit('custom-event', 'Hello from Child');
}
}
};
</script>
現(xiàn)在,在父組件中,你可以監(jiān)聽子組件觸發(fā)的 custom-event 事件:
<!-- ParentComponent.vue -->
<template>
<ChildComponent @custom-event="handleCustomEvent" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
methods: {
handleCustomEvent(data) {
console.log('Custom Event Handled in Parent:', data);
}
},
components: {
ChildComponent
}
};
</script>
在這個例子中,v-on="$listeners" 將所有父組件傳遞的事件監(jiān)聽器應(yīng)用到了按鈕上,這樣子組件就能夠觸發(fā)相應(yīng)的事件并將數(shù)據(jù)傳遞給父組件。
2. 兄弟組件通信:
2.1 通過共享狀態(tài)(使用父組件):
通過將狀態(tài)提升到共同的父組件,然后通過 props 和事件來實現(xiàn)兄弟組件之間的通信。
2.2 使用事件總線(Bus):
創(chuàng)建一個事件總線,兄弟組件通過事件總線來通信。在 Vue 3 中,可以使用 provide/inject 來創(chuàng)建一個簡單的事件總線。
// EventBus.js
import { createApp, ref } from 'vue';
const bus = createApp();
bus.provide('eventBus', bus);
// ComponentA.vue
<script>
export default {
methods: {
notifySibling() {
this.$root.eventBus.emit('siblingEvent', 'Hello from Component A!');
}
}
};
</script>
// ComponentB.vue
<script>
export default {
created() {
this.$root.eventBus.on('siblingEvent', message => {
console.log(message); // 處理從兄弟組件傳遞的數(shù)據(jù)
});
}
};
</script>
3. 祖孫組件通信:
3.1 通過 provide/inject:
使用 provide/inject 可以實現(xiàn)祖孫組件之間的通信,祖先組件通過 provide 提供數(shù)據(jù),孫子組件通過 inject 獲取數(shù)據(jù)。
<!-- Grandparent.vue -->
<template>
<Parent />
</template>
<script>
import { ref } from 'vue';
import Parent from './Parent.vue';
export default {
setup() {
const grandparentMessage = ref('Hello from Grandparent!');
provide('grandparentMessage', grandparentMessage);
return {
grandparentMessage
};
},
components: {
Parent
}
};
</script>
<!-- Parent.vue -->
<template>
<Child />
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child
}
};
</script>
<!-- Child.vue -->
<template>
<div>{{ grandparentMessage }}</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const grandparentMessage = inject('grandparentMessage');
return {
grandparentMessage
};
}
};
</script>
4. Vuex(全局狀態(tài)管理):
如果你的應(yīng)用中需要管理全局狀態(tài),Vuex 是一個強大的狀態(tài)管理庫。通過將狀態(tài)存儲在 Vuex 中,不同組件可以通過 Vuex 的 store 來實現(xiàn)通信。
// store.js
import { createStore } from 'vuex';
export default createStore({
state: {
message: 'Hello from Vuex!'
},
mutations: {
updateMessage(state, newMessage) {
state.message = newMessage;
}
},
actions: {
changeMessage({ commit }, newMessage) {
commit('updateMessage', newMessage);
}
}
});
// ComponentA.vue
<script>
export default {
computed: {
message() {
return this.$store.state.message;
}
},
methods: {
changeMessage() {
this.$store.dispatch('changeMessage', 'New Message');
}
}
};
</script>
// ComponentB.vue
<script>
export default {
computed: {
message() {
return this.$store.state.message;
}
}
};
</script>
在 Vue.js 生態(tài)系統(tǒng)中,除了 $listeners 之外,還有一些其他狀態(tài)管理庫,其中包括 Pinia 和 Tini。這兩個庫提供了不同的方式來處理狀態(tài)管理,和 Vuex 一樣,它們都是 Vue 3 的狀態(tài)管理庫。
5. Pinia:
Pinia 是一個由 Vue.js 團隊開發(fā)的狀態(tài)管理庫,旨在提供簡單、靈活且性能出色的狀態(tài)管理方案。與 Vuex 不同,Pinia 使用了更現(xiàn)代的 API,并且是基于 Composition API 構(gòu)建的。
Pinia 的特點:
- 使用 Composition API 構(gòu)建。
- 具有 TypeScript 支持。
- 基于 Vue 3 的響應(yīng)式系統(tǒng)。
- 使用插件系統(tǒng)輕松擴展功能。
安裝 Pinia:
npm install pinia
使用 Pinia:
import { createPinia } from 'pinia';
const pinia = createPinia();
export const useStore = pinia.createStore({
state: () => ({ count: 0 }),
actions: {
increment() {
this.state.count++;
}
}
});
6.mitt
mitt 是一個簡單而小巧的事件總線庫,用于在應(yīng)用程序的不同部分之間進行事件通信。它提供了一種簡單的方式來發(fā)射和監(jiān)聽事件。與 Vuex 的 $emit 和 $on 類似,但 mitt 更為輕量。
下面是 mitt 的基本用法:
import mitt from 'mitt';
// 創(chuàng)建事件總線
const emitter = mitt();
// 監(jiān)聽事件
emitter.on('custom-event', (data) => {
console.log('Event Received:', data);
});
// 發(fā)射事件
emitter.emit('custom-event', 'Hello from Mitt!');
在上面的代碼中,emitter 是一個事件總線實例,可以用于在不同部分之間發(fā)送和接收事件。