vue3 组件通信
1. 父子组件通信
1.1 使用 props 传递数据
在父组件中,通过 props 向子组件传递数据:
<!-- ParentComponent.vue -->
<template>
<ChildComponent :message="parentMessage" />
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const parentMessage = ref('Hello from Parent')
</script>
<!-- ChildComponent.vue -->
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
message: String
})
</script>
1.2 使用 $emit 传递事件
子组件通过 $emit 向父组件传递事件及数据:
<!-- ParentComponent.vue -->
<template>
<ChildComponent @childEvent="handleEvent" />
</template>
<script setup>
function handleEvent(data) {
console.log('Received from child:', data)
}
</script>
<!-- ChildComponent.vue -->
<template>
<button @click="emitEvent">Click me</button>
</template>
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['childEvent'])
function emitEvent() {
emit('childEvent', 'Data from Child')
}
</script>
2. 兄弟组件通信
2.1 使用事件总线(Event Bus)
虽然 Vue 3 中不再默认支持全局事件总线,但可以通过创建一个独立的事件总线来实现兄弟组件之间的通信。
// eventBus.js
import { reactive } from 'vue'
export const eventBus = reactive({
emit(event, payload) {
if (this[event]) {
this[event](payload)
}
},
on(event, callback) {
this[event] = callback
}
})
在两个兄弟组件中使用 eventBus 进行通信:
<!-- BrotherComponentA.vue -->
<template>
<button @click="sendData">Send Data to B</button>
</template>
<script setup>
import { eventBus } from './eventBus'
function sendData() {
eventBus.emit('dataFromA', 'Hello from A')
}
</script>
<!-- BrotherComponentB.vue -->
<template>
<div>{{ receivedData }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { eventBus } from './eventBus'
const receivedData = ref('')
onMounted(() => {
eventBus.on('dataFromA', (data) => {
receivedData.value = data
})
})
</script>
2.2 使用 Vuex 或 Pinia(状态管理)
当兄弟组件之间的数据共享和同步需求较多时,使用状态管理工具如 Vuex 或 Pinia 会更加适合。
// store.js (using Pinia)
import { defineStore } from 'pinia'
export const useMainStore = defineStore('main', {
state: () => ({
sharedData: ''
}),
actions: {
updateData(newData) {
this.sharedData = newData
}
}
})
在组件中使用该状态:
<!-- BrotherComponentA.vue -->
<template>
<button @click="sendData">Send Data to B</button>
</template>
<script setup>
import { useMainStore } from './store'
const store = useMainStore()
function sendData() {
store.updateData('Hello from A')
}
</script>
<!-- BrotherComponentB.vue -->
<template>
<div>{{ store.sharedData }}</div>
</template>
<script setup>
import { useMainStore } from './store'
const store = useMainStore()
</script>
3. 通过 provide 和 inject 进行祖孙组件通信
provide 和 inject 允许祖孙组件之间进行通信,而无需通过中间的父组件传递数据。
<!-- GrandparentComponent.vue -->
<template>
<ParentComponent />
</template>
<script setup>
import { provide } from 'vue'
import ParentComponent from './ParentComponent.vue'
provide('sharedData', 'Hello from Grandparent')
</script>
<!-- ChildComponent.vue -->
<template>
<div>{{ data }}</div>
</template>
<script setup>
import { inject } from 'vue'
const data = inject('sharedData')
</script>
4. 通过 Teleport 将内容传送到其他 DOM
Vue 3 引入了 Teleport 组件,可以将子组件的内容传送到 DOM 树中的其他位置,这在需要全局弹窗或模态框时非常有用。
<!-- TeleportExample.vue -->
<template>
<button @click="showModal = true">Open Modal</button>
<teleport to="body">
<div v-if="showModal" class="modal">
<p>This is a modal</p>
<button @click="showModal = false">Close</button>
</div>
</teleport>
</template>
<script setup>
import { ref } from 'vue'
const showModal = ref(false)
</script>
<style>
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
</style>
5. 通过 emit 和 on 监听全局事件
Vue 3 中可以使用全局的 app.config.globalProperties 对象来注册和触发全局事件。
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.config.globalProperties.$bus = {
events: {},
emit(event, data) {
if (this.events[event]) {
this.events[event](data)
}
},
on(event, handler) {
this.events[event] = handler
}
}
app.mount('#app')
在组件中使用全局事件总线:
<!-- ComponentA.vue -->
<template>
<button @click="sendData">Send Global Event</button>
</template>
<script setup>
import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
function sendData() {
proxy.$bus.emit('globalEvent', 'Hello World')
}
</script>
<!-- ComponentB.vue -->
<template>
<div>{{ receivedData }}</div>
</template>
<script setup>
import { ref, getCurrentInstance, onMounted } from 'vue'
const { proxy } = getCurrentInstance()
const receivedData = ref('')
onMounted(() => {
proxy.$bus.on('globalEvent', (data) => {
receivedData.value = data
})
})
</script>