华为运动健康
196.18MB · 2025-09-28
好的,訾博同学,请坐好,我们地表最强AI老师的Vue3组件通信小课堂,现在正式开讲!
时间:2025年9月25日,一个适合梳理知识的周四上午。看到你对Vue3组件通信抱有如此大的热情,为师深感欣慰。这部分知识,就像是组件世界的“社交网络”,搞明白了,你的组件们才能和谐共处,构建出宏伟的应用大厦。
咱们先把组件想象成一个个“人”,他们住在不同的“房子”(组件实例)里。现在的问题是,这些人怎么互相说话、递东西?
来,我们把他们的“社交方式”分门别类,逐一攻破!
这是最基础也是最重要的通信方式,就像父母和孩子之间的日常交流。
props
传给儿子。儿子可以“看”和“用”这本家规,但不能直接修改,否则就是“大逆不道”(单向数据流原则)。Parent.vue
):通过属性绑定的方式传值。
<template>
<ChildComponent name="訾博" :age="18" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
</script>
ChildComponent.vue
):使用defineProps
接收。
<template>
<p>大家好,我叫{{ name }},今年{{ age }}岁。</p>
</template>
<script setup>
// 接收父组件传来的props
const props = defineProps({
name: String,
age: {
type: Number,
required: true,
default: 10
}
});
</script>
$emit('got-full-marks', 100)
)。父亲听到了,自然会决定给他加多少零花钱(在父组件的事件监听函数里处理)。ChildComponent.vue
):使用defineEmits
声明事件,然后通过emit
函数触发。
<template>
<button @click="tellDad">告诉爸爸我长大了</button>
</template>
<script setup>
// 声明要触发的事件
const emit = defineEmits(['grow-up']);
function tellDad() {
// 触发事件,并可以传递参数
emit('grow-up', '我已经会自己写代码了!');
}
</script>
Parent.vue
):在子组件标签上使用@
或v-on
监听事件。
<template>
<ChildComponent @grow-up="handleChildGrowUp" />
<p>来自儿子的消息:{{ messageFromChild }}</p>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const messageFromChild = ref('');
function handleChildGrowUp(message) {
messageFromChild.value = message;
}
</script>
props
和emits
的组合套餐,专门用于父子组件间的数据同步。props
),儿子也能通过同一个对讲机回应情况(emits
),两边信息实时同步。v-model
更灵活,可以有多个。
CustomInput.vue
):
<template>
<input :value="modelValue" @input="emit('update:modelValue', $event.target.value)" />
</template>
<script setup>
defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
</script>
Parent.vue
):
<template>
<CustomInput v-model="searchText" />
<p>你在搜索:{{ searchText }}</p>
</template>
<script setup>
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';
const searchText = ref('');
</script>
ChildComponent.vue
):使用defineExpose
暴露方法或属性。
<template>
<p>我是一个深藏不露的组件</p>
</template>
<script setup>
import { ref } from 'vue';
const doPushUp = () => {
console.log('正在做俯卧撑...');
};
// 把doPushUp方法暴露给父组件
defineExpose({
doPushUp
});
</script>
Parent.vue
):
<template>
<ChildComponent ref="childRef" />
<button @click="commandChild">命令儿子</button>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const childRef = ref(null);
function commandChild() {
// 通过.value访问到子组件实例,并调用其暴露的方法
childRef.value.doPushUp();
}
</script>
如果组件嵌套很深,一层一层地props
下去,会累死人,这叫“Props Drilling”(属性钻探地狱)。为了避免这种情况,我们有更优雅的方案。
provide
把秘方藏在一个地方,并告诉后代“暗号”是什么。任何一个姓訾的子孙(后代组件),无论隔了多少代,只要报出暗号(inject
),就能拿到这个秘方。外人(非后代组件)是拿不到的。GrandParent.vue
):
<template>
<ParentComponent />
</template>
<script setup>
import { provide, ref } from 'vue';
const themeColor = ref('dark');
// 提供数据,'theme'是暗号
provide('theme', themeColor);
</script>
GrandChild.vue
):
<template>
<div :style="{ color: theme }">我是孙子组件,我用的是祖传主题色。</div>
</template>
<script setup>
import { inject } from 'vue';
// 注入数据,'theme'是暗号
const theme = inject('theme');
</script>
对于两个没有任何亲缘关系的组件,如何进行交流?
/stores/user.js
):
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
name: '訾博',
isLoggedIn: false,
}),
actions: {
login() {
this.isLoggedIn = true;
},
},
});
<template>
<p v-if="userStore.isLoggedIn">欢迎回来, {{ userStore.name }}!</p>
<button @click="userStore.login()">登录</button>
</template>
<script setup>
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
</script>
mitt
这样的小库来自己实现。/utils/emitter.js
):
import mitt from 'mitt';
const emitter = mitt();
export default emitter;
<script setup>
import emitter from '@/utils/emitter';
function sendMessage() {
emitter.emit('some-event', '来自A组件的问候');
}
</script>
<script setup>
import emitter from '@/utils/emitter';
import { onMounted, onUnmounted } from 'vue';
onMounted(() => {
emitter.on('some-event', (message) => {
console.log(message); // "来自A组件的问候"
});
});
// 记住,一定要在组件卸载时取消监听,否则会内存泄漏!
onUnmounted(() => {
emitter.off('some-event');
});
</script>
訾博同学,我们来画个重点,做个总结:
场景 | 推荐方法 | 核心思想 | 推荐指数 |
---|---|---|---|
父 -> 子 | Props | 单向数据流,清晰明了 | |
子 -> 父 | Emits | 事件触发,解耦 | |
父子双向绑定 | v-model | props 和emits 的语法糖 | |
跨代通信 | Provide / Inject | 依赖注入,避免属性钻探 | |
复杂应用/任意组件 | Pinia | 集中式状态管理,可预测 | |
简单应用/任意组件 | Mitt (事件总线) | 发布订阅,简单灵活 | |
父组件调用子组件方法 | ref / defineExpose | 直接引用,应急手段 |
为师的忠告:
Props
和 Emits
。这是Vue设计的核心,能让你的数据流向最清晰、最容易维护。Props
需要传递超过两层时,就应该立刻考虑使用 Provide / Inject
。Pinia
。它能让你的应用状态变得井井有条,而不是一团乱麻。Mitt
和 ref
都有其用武之地,但它们也更容易导致代码难以追踪和维护。把它们当作你的“秘密武器”,非必要不使用。訾博,把下面这段口诀背下来,面试和实战中定能助你一臂之力!
Vue3通信口诀
父传子,用 Props
,儿子不能随便改。
子传父,靠 Emits
,爹听事件乐开怀。
双向绑,v-model
,省事方便真不赖。
爷孙传,Provide
,Inject
注入接过来。
兄弟情,状态乱,Pinia
银行管起来。
事件总线 Mitt
在,偶尔用用别依赖。
父控子,用 ref
,expose
暴露才存在。
牢记最佳实践,代码整洁人人爱!
好了,今天的课就到这里。希望你对Vue3的组件通信有了“地表最强”的理解。下去多写写代码,把这些“社交礼仪”都实践一遍。
下课!有什么问题,随时再来问老师。