组件传值分为:父子传值 、跨组件传值
| 通信方式 | 适用场景 | 核心 API / 机制 |
|---|---|---|
| Props | 父 → 子 | defineProps |
| Emits | 子 → 父 | defineEmits |
| v-model | 父子双向绑定 | modelValue + update:modelValue |
| Provide/Inject | 祖先 → 深层后代 | provide + inject |
| 事件总线 | 兄弟 / 任意组件(简单场景) | mitt 等第三方库 |
| 状态管理 | 复杂共享状态(全局 / 跨组件) | Pinia/Vuex |
| Refs | 父访问子组件实例 / DOM(谨慎用) | ref + defineExpose |
1. 父传子
父组件通过props将数据传递给子组件,子组件通过defineProps进行接收并使用
javascript
父组件 Parent
┌───────────────────────────────┐
│ data: { message: "Hello Vue3" } │
│ │
│ <Child :msg="message" /> │
└───────────────────────────────┘
│
▼ (props传递)
┌───────────────────────────────┐
│ 子组件 Child │
│ │
│ defineProps({ │
│ msg: String │
│ }) │
│ │
│ <div>{{ msg }}</div> → 显示: Hello Vue3 │
└───────────────────────────────┘
2. 子向父传值
子组件通过defineEmits定义事件,使用emit触发事件,父组件监听事件并处理数据
javascript
子组件 Child
┌───────────────────────────────┐
│ defineEmits(['updateMsg']) │
│ │
│ <button @click="sendMsg"> │
│ 发送消息给父组件 │
│ </button> │
│ │
│ function sendMsg() { │
│ emit('updateMsg', 'Hi Dad!')│
│ } │
└───────────────────────────────┘
│
▼ (事件触发)
┌───────────────────────────────────────┐
│ 父组件 Parent │
│ │
│ <Child @update-msg="handleUpdate" /> │
│ │
│ function handleUpdate(data) { │
│ message.value = data │
│ } → message变为: Hi Dad! │
└───────────────────────────────────────┘
javascript
<!-- Child.vue -->
<template>
<div class="child">
<h3>子组件</h3>
<input v-model="childMsg" placeholder="输入要发送的消息" />
<button @click="sendToParent">发送给父组件</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const childMsg = ref('')
const emit = defineEmits(['updateMsg'])
const sendToParent = () => {
emit('updateMsg', childMsg.value)
childMsg.value = ''
}
</script>
<!-- Parent.vue -->
<template>
<div class="parent">
<h2>父组件</h2>
<p>从子组件接收: {{ receivedMsg }}</p>
<Child @update-msg="handleUpdate" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const receivedMsg = ref('')
const handleUpdate = (msg) => {
receivedMsg.value = msg
}
</script>
3. 父子双向绑定
待写待更新
4. 跨组件传值:Provide/Inject
祖先组件通过provide提供数据,后代组件通过inject注入数据,实现跨层级传递
javascript
<!-- Grandparent.vue -->
<template>
<div class="grandparent" :class="theme">
<h2>祖先组件</h2>
<select v-model="theme">
<option value="light">浅色模式</option>
<option value="dark">深色模式</option>
</select>
<Parent />
</div>
</template>
<script setup>
import { ref, provide } from 'vue'
import Parent from './Parent.vue'
const theme = ref('light')
provide('theme', theme) // 提供响应式数据
</script>
<style>
.light { background: white; color: black; }
.dark { background: #333; color: white; }
</style>
<!-- Parent.vue -->
<template>
<div class="parent">
<h3>父组件</h3>
<Child />
</div>
</template>
<script setup>
import Child from './Child.vue'
</script>
<!-- Child.vue -->
<template>
<div class="child">
<h4>子组件</h4>
<p>当前主题: {{ theme }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue'
const theme = inject('theme') // 注入响应式数据
</script>
5. 全局状态管理:Pinia
对于复杂应用使用Pinia进行管理全局状态,任何组件都可以访问和修改
javascript
Pinia Store
┌───────────────────────────────┐
│ state: { user: null } │
│ actions: { login, logout } │
└───────────────────────────────┘
│
▼ (组件A访问)
┌───────────────────────────────┐
│ 组件A │
│ store.login(userInfo) │
└───────────────────────────────┘
│
▼ (组件B访问)
┌───────────────────────────────┐
│ 组件B │
│ store.user → 获取用户信息 │
└───────────────────────────────┘
javascript
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
user: null
}),
actions: {
login(userInfo) {
this.user = userInfo
},
logout() {
this.user = null
}
}
})
javascript
<!-- Login.vue -->
<template>
<div>
<input v-model="username" placeholder="用户名" />
<button @click="handleLogin">登录</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useUserStore } from './stores/user'
const username = ref('')
const userStore = useUserStore()
const handleLogin = () => {
userStore.login({ name: username.value })
}
</script>
<!-- Profile.vue -->
<template>
<div>
<h3>用户信息</h3>
<p v-if="userStore.user">欢迎, {{ userStore.user.name }}</p>
<p v-else>请先登录</p>
<button v-if="userStore.user" @click="userStore.logout">退出</button>
</div>
</template>
<script setup>
import { useUserStore } from './stores/user'
const userStore = useUserStore()
</script>