组件之间怎么传数据?
<!-- Parent.vue -->
<template>
<Child name="Tom" :age="20" :isActive="true" />
</template>
<script setup>
import Child from './Child.vue'
</script><!-- Child.vue -->
<script setup>
defineProps({
name: String,
age: {
type: Number,
required: true,
default: 18
},
isActive: Boolean
})
</script>
<template>
<div>{{ name }} - {{ age }} - {{ isActive }}</div>
</template><!-- Child.vue -->
<script setup>
const emit = defineEmits(['update', 'delete'])
function handleClick() {
emit('update', { id: 1, name: 'Tom' })
}
</script>
<template>
<button @click="handleClick">更新</button>
</template><!-- Parent.vue -->
<template>
<Child @update="handleUpdate" />
</template>
<script setup>
function handleUpdate(data) {
console.log('收到:', data)
}
</script><!-- Parent.vue -->
<template>
<Child v-model="count" />
<!-- 等价于 -->
<Child :modelValue="count" @update:modelValue="count = $event" />
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script><!-- Child.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template><!-- Parent.vue -->
<template>
<Child v-model:name="name" v-model:age="age" />
</template>
<script setup>
import { ref } from 'vue'
const name = ref('Tom')
const age = ref(20)
</script><!-- Child.vue -->
<script setup>
defineProps(['name', 'age'])
defineEmits(['update:name', 'update:age'])
</script><!-- Child.vue -->
<script setup>
function getData() {
return 'child data'
}
defineExpose({ getData })
</script>
<template>
<div>Child</div>
</template><!-- Parent.vue -->
<template>
<Child ref="childRef" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
const childRef = ref(null)
onMounted(() => {
console.log(childRef.value.getData()) // 'child data'
})
</script><!-- Parent.vue -->
<template>
<A @change="handleChange" :value="value" />
<B :value="value" />
</template>
<script setup>
import { ref } from 'vue'
import A from './A.vue'
import B from './B.vue'
const value = ref('')
function handleChange(val) {
value.value = val
}
</script>// eventBus.js
import mitt from 'mitt'
export const emitter = mitt()<!-- A.vue -->
<script setup>
import { emitter } from './eventBus'
function send() {
emitter.emit('message', 'hello from A')
}
</script><!-- B.vue -->
<script setup>
import { onUnmounted } from 'vue'
import { emitter } from './eventBus'
emitter.on('message', (msg) => {
console.log(msg)
})
onUnmounted(() => {
emitter.off('message')
})
</script>// store/useUserStore.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: 'Tom'
}),
actions: {
updateName(name) {
this.name = name
}
}
})<!-- A.vue -->
<script setup>
import { useUserStore } from './store/useUserStore'
const store = useUserStore()
function change() {
store.updateName('Jerry')
}
</script><!-- B.vue -->
<script setup>
import { useUserStore } from './store/useUserStore'
const store = useUserStore()
</script>
<template>
<div>{{ store.name }}</div>
</template><!-- 祖先组件 -->
<script setup>
import { provide, ref } from 'vue'
const count = ref(0)
provide('count', count)
// 也可以传静态值
provide('theme', 'dark')
</script><!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const count = inject('count')
const theme = inject('theme')
function add() {
count.value++
}
</script>// ❌ 非响应式 - 后代拿不到最新值
provide('user', { name: 'Tom' })
// ✅ 响应式 - 用 reactive 或 ref
import { reactive, ref } from 'vue'
const user = reactive({ name: 'Tom' })
provide('user', user)
// ✅ 或者只传 ref
const name = ref('Tom')
provide('name', name)<!-- Parent.vue -->
<template>
<Child class="custom" style="color: red" data-id="1" />
</template><!-- Child.vue -->
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
console.log(attrs.class) // 'custom'
console.log(attrs.style) // { color: 'red' }
console.log(attrs['data-id']) // '1'
</script>
<template>
<!-- 透传给孙组件 -->
<GrandChild v-bind="$attrs" />
</template><!-- Child.vue -->
<script setup>
defineProps(['title'])
const attrs = useAttrs()
</script>
<template>
<div :class="attrs.class">
<GrandChild
v-bind="$attrs"
@click="attrs.onClick"
/>
</div>
</template>| 场景 | 方式 | 备注 |
|---|---|---|
| 父子 Props | 父→子 | 单向,只读 |
| 父子 Emit | 子→父 | 事件机制 |
| 父子 v-model | 双向绑定 | 语法糖 |
| 父子 Ref | 父调子方法 | 暴露 API |
| 兄弟 | 父组件中转 | 简单场景 |
| 兄弟 | Mitt | 事件总线 |
| 兄弟 | Pinia | 状态管理 |
| 跨层级 | Provide/Inject | 祖先→后代 |
| 跨层级 | $attrs | 透传 |
| 全局 | Pinia | 任意组件 |