Vue3 组件通信
1.【props】父传子
核心概念:父组件通过属性传递数据给子组件
<!-- 父组件 -->
<template>
<Child :car="car" :getToy="getToy"/>
</template>
<script setup>
import { ref } from "vue"
const car = ref('奔驰')
function getToy(value) {
console.log('收到子组件的玩具:', value)
}
</script>
<!-- 子组件 -->
<script setup>
// 接收props
defineProps(['car','getToy'])
</script>
使用场景:
- 父组件向子组件传递数据
- 子组件向父组件传递数据(通过传递函数)
2.【自定义事件】子传父
核心概念:子组件通过事件向父组件传递数据
<!-- 父组件监听事件 -->
<template>
<Child @send-toy="handleToy"/>
</template>
<script setup>
const handleToy = (toy) => {
console.log('收到玩具:', toy)
}
</script>
<!-- 子组件触发事件 -->
<script setup>
const emit = defineEmits(['send-toy'])
const sendData = () => {
emit('send-toy', '奥特曼')
}
</script>
3.【mitt】任意组件通信
核心概念:全局事件总线,任意组件间通信
// utils/emitter.ts
import mitt from "mitt"
const emitter = mitt()
export default emitter
// 组件A - 发送事件
import emitter from "@/utils/emitter"
emitter.emit('send-data', data)
// 组件B - 接收事件
import emitter from "@/utils/emitter"
emitter.on('send-data', (data) => {
console.log('收到数据:', data)
})
4.【v-model】双向绑定
核心概念:简化父子组件双向数据绑定
<!-- 父组件 -->
<template>
<CustomInput v-model="username"/>
</template>
<!-- 子组件 -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
5.【$attrs】祖传孙
核心概念:父组件属性自动传递给子组件
<!-- 父组件 -->
<template>
<Child :a="a" :b="b" :c="c"/>
</template>
<!-- 子组件直接传递 -->
<template>
<GrandChild v-bind="$attrs"/>
</template>
<!-- 孙组件接收 -->
<script setup lang="ts" name="GrandChild">
defineProps(['a','b','c','d','x','y','updateA'])
</script>
6.【$refs、$parent】直接访问
核心概念:直接访问组件实例 $refs、
<script setup>
// 父组件访问子组件
const childRef = ref()
childRef.value.someMethod()
// 子组件访问父组件
const parent = getCurrentInstance().parent
</script>
7.【provide/inject】依赖注入
核心概念:祖先组件提供数据,后代组件注入使用
<!-- 祖先组件 -->
<script setup>
import { provide, ref } from 'vue'
const money = ref(100)
provide('money', money)
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const money = inject('money')
</script>
8.【slot】插槽
核心概念:父组件向子组件传递模板内容
8.1.默认插槽
父组件中:
<Category title="今日热门游戏">
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
</Category>
子组件中:
<template>
<div class="item">
<h3>{{ title }}</h3>
<!-- 默认插槽 -->
<slot></slot>
</div>
</template>
8.2.具名插槽
父组件中:
<Category title="今日热门游戏">
<template v-slot:s1>
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
</template>
<template #s2>
<a href="">更多</a>
</template>
</Category>
子组件中:
<template>
<div class="item">
<h3>{{ title }}</h3>
<slot name="s1"></slot>
<slot name="s2"></slot>
</div>
</template>
8.3.作用域插槽
父组件中:
<Game v-slot="params">
<!-- <Game v-slot:default="params"> -->
<!-- <Game #default="params"> -->
<ul>
<li v-for="g in params.games" :key="g.id">{{ g.name }}</li>
</ul>
</Game>
子组件中:
<template>
<div class="category">
<h2>今日游戏榜单</h2>
<slot :games="games" a="哈哈"></slot>
</div>
</template>
<script setup lang="ts" name="Category">
import {reactive} from 'vue'
let games = reactive([
{id:'asgdytsa01',name:'英雄联盟'},
{id:'asgdytsa02',name:'王者荣耀'},
{id:'asgdytsa03',name:'红色警戒'},
{id:'asgdytsa04',name:'斗罗大陆'}
])
</script>
9.【Pinia】状态管理
9.1.为什么需要 Pinia?
问题:当多个组件需要共享相同数据时,props 逐层传递太繁琐
解决方案:Pinia 提供全局状态管理,任何组件都可以直接访问
9.2.三大核心概念
9.2.1.State - 存储数据
export const useStore = defineStore('store', {
state: () => ({
count: 0,
user: null
})
})
9.2.2.Getters - 计算属性
getters: {
doubleCount: (state) => state.count * 2
}
9.2.3. Actions - 操作方法
actions: {
increment() {
this.count++
}
}
10.使用场景对比
| 通信方式 | 适用场景 | 数据流向 |
|---|---|---|
| props | 父子组件简单数据传递 | 父 → 子 |
| emit | 子组件向父组件发送消息 | 子 → 父 |
| mitt | 任意无关组件通信 | 任意方向 |
| v-model | 表单组件双向绑定 | 父子双向 |
| provide/inject | 深层嵌套组件传值 | 祖先 → 后代 |
| $refs/$parent | 父(子)组件访问子(父)组件实例 | 父 → 子(子 → 父) |
| slot | 父组件向子组件传递模板 | 父 → 子 |
| Pinia | 多个组件共享复杂状态 | 全局共享 |
**选择建议 **
- 简单父子通信 → props + emit
- 无关组件通信 → mitt
- 深层嵌套传值 → provide/inject
- 复杂状态共享 → Pinia
- 表单组件 → v-model
- 传递模板内容 → slot
- 直接访问组件实例 → $refs、$parent
记住:不要为了用 Pinia 而用 Pinia,简单的组件通信用 props 和 emit 就足够了!
1248

被折叠的 条评论
为什么被折叠?



