Vue3 组件通信
Props / Emits
父传子(Props)
<!-- 父组件 -->
<template>
<ChildComponent :message="parentMessage" />
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const parentMessage = ref('Hello from parent')
</script>
<!-- 子组件 -->
<script setup>
defineProps({
message: String
})
</script>
子传父(Emits)
<!-- 子组件 -->
<script setup>
const emit = defineEmits(['update'])
const handleClick = () => {
emit('update', { data: 'some data' })
}
</script>
<!-- 父组件 -->
<template>
<ChildComponent @update="handleUpdate" />
</template>
<script setup>
const handleUpdate = (payload) => {
console.log(payload)
}
</script>
v-model
单个 v-model
<!-- 子组件 -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<!-- 父组件 -->
<template>
<ChildComponent v-model="text" />
</template>
多个 v-model
<!-- 子组件 -->
<script setup>
defineProps(['firstName', 'lastName'])
defineEmits(['update:firstName', 'update:lastName'])
</script>
<!-- 父组件 -->
<template>
<ChildComponent
v-model:first-name="first"
v-model:last-name="last"
/>
</template>
provide / inject
<!-- 父组件 -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)
// 提供响应式数据
const updateTheme = (newTheme) => {
theme.value = newTheme
}
provide('updateTheme', updateTheme)
</script>
<!-- 子孙组件 -->
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
const updateTheme = inject('updateTheme')
</script>
提供默认值
const theme = inject('theme', 'light')
使用 Symbol
// keys.js
export const themeKey = Symbol()
// 父组件
import { themeKey } from './keys'
provide(themeKey, theme)
// 子组件
import { themeKey } from './keys'
const theme = inject(themeKey)
Refs
模板引用
<script setup>
import { ref, onMounted } from 'vue'
const childRef = ref(null)
onMounted(() => {
childRef.value.someMethod()
})
</script>
<template>
<ChildComponent ref="childRef" />
</template>
defineExpose
<!-- 子组件 -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
// 显式暴露
defineExpose({
count,
increment
})
</script>
EventBus(不推荐)
Vue3 移除了 $on、$off 等方法,推荐使用第三方库如 mitt。
yarn add mitt
// eventBus.js
import mitt from 'mitt'
export const emitter = mitt()
// 组件 A
import { emitter } from './eventBus'
emitter.emit('event-name', data)
// 组件 B
import { emitter } from './eventBus'
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
emitter.on('event-name', handleEvent)
})
onUnmounted(() => {
emitter.off('event-name', handleEvent)
})
Pinia
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
}
}
})
// 组件中使用
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
counter.increment()