作为 Vue 初学者,组件通信 和插槽是实现组件复用、解耦的核心技能。这篇笔记会把这两个知识点拆解成 "一看就懂" 的形式,既是自己的学习总结,也希望能帮到同样入门的同学~
一、组件通信:父子组件的数据传递
Vue 中组件是独立的,想要实现 "组件之间传数据",核心有两种场景:父传子(Props) 和 子传父(自定义事件) 。
1. 父传子:Props 传递数据(单向数据流)
父组件通过props把数据传递给子组件,子组件只能 "读取" 不能 "直接修改"(Vue 的单向数据流规则)。
示例:父组件ComponentA向子组件ComponentB传值
vue
xml
<!-- ComponentA.vue(父组件) -->
<template>
<h3>ComponentA</h3>
<!-- 第一步:在子组件标签上绑定要传递的数据 -->
<ComponentB :title="title" :age="age" :names="names"/>
</template>
<script>
import ComponentB from "./ComponentB.vue"
export default {
components: { ComponentB },
data() {
return {
title: "父组件传递的标题",
age: 20,
names: ["iwen", "ime"]
}
}
}
</script>
vue
xml
<!-- ComponentB.vue(子组件) -->
<template>
<p>{{ title }}</p>
<p>{{ age }}</p>
<p v-for="(item,index) of names" :key="index">{{ item }}</p>
</template>
<script>
export default {
// 第二步:子组件通过props选项声明接收的数据
props: ["title", "age", "names"]
}
</script>
进阶:Props 的类型、默认值、必填校验 实际开发中,我们可以给props加 "类型限制、默认值、必填校验",让组件更健壮。
vue
xml
<!-- ComponentB.vue(子组件) -->
<script>
export default {
props: {
// 限制title的类型可以是字符串、数字、数组、对象
title: {
type: [String, Number, Array, Object],
required: true // 表示title是必填项
},
// 限制age的类型是数字,默认值为0
age: {
type: Number,
default: 0
},
// 数组/对象的默认值必须用工厂函数返回
names: {
type: Array,
default() {
return ["默认名称"]
}
}
}
}
</script>
2. 子传父:自定义事件($emit)传递数据
子组件通过this.$emit("事件名", 数据)触发自定义事件,父组件通过@事件名监听并接收数据。
示例:子组件Child向父组件ComponentEvent传值
vue
xml
<!-- Child.vue(子组件) -->
<template>
<h3>Child</h3>
<button @click="clickEventHandle">传递数据给父组件</button>
</template>
<script>
export default {
data() {
return {
msg: "子组件的私有数据~"
}
},
methods: {
clickEventHandle() {
// 触发自定义事件,并传递数据msg
this.$emit("someEvent", this.msg)
}
}
}
</script>
vue
xml
<!-- ComponentEvent.vue(父组件) -->
<template>
<h3>组件事件</h3>
<!-- 监听子组件的someEvent事件,触发getHandle方法 -->
<Child @someEvent="getHandle"/>
<p>父组件接收的子组件数据:{{ message }}</p>
</template>
<script>
import Child from "./Child.vue"
export default {
components: { Child },
data() {
return {
message: ""
}
},
methods: {
getHandle(data) {
// 接收子组件传递的数据,并赋值给message
this.message = data
}
}
}
</script>
二、插槽(Slot):让组件更灵活的 "内容插槽"
插槽的作用是让父组件可以向子组件 "注入" 自定义内容 ,实现组件的灵活复用。Vue 的插槽分为三种:默认插槽、具名插槽、作用域插槽。
1. 默认插槽:最简单的 "内容注入"
子组件用<slot></slot>预留一个 "内容坑位",父组件在子组件标签内写的内容会自动填入这个坑位。
示例:父组件App向子组件SlotsTow注入内容
vue
xml
<!-- SlotsTow.vue(子组件) -->
<template>
<h3>Slots续集</h3>
<!-- 预留默认插槽的坑位 -->
<slot></slot>
</template>
vue
xml
<!-- App.vue(父组件) -->
<template>
<SlotsTow>
<!-- 父组件在这里写的内容,会被注入到子组件的slot中 -->
<h3>我是父组件注入的内容</h3>
<p>插槽让组件变得更灵活!</p>
</SlotsTow>
</template>
<script>
import SlotsTow from "./components/SlotsTow.vue"
export default {
components: { SlotsTow }
}
</script>
默认内容: 如果父组件没注入内容,子组件可以给插槽设置 "默认内容"。
vue
xml
<!-- SlotsTow.vue(子组件) -->
<template>
<h3>Slots续集</h3>
<!-- 没内容时显示"默认内容" -->
<slot>父组件没传内容,我是默认显示的文字~</slot>
</template>
2. 具名插槽:多个 "命名坑位" 的精准注入
当子组件需要多个插槽坑位 时,用name给每个插槽命名,父组件用v-slot:名称(或#名称)指定内容注入到哪个坑位。
示例:子组件SlotsAttr有header和main两个具名插槽
vue
xml
<!-- SlotsAttr.vue(子组件) -->
<template>
<div>
<h3>具名插槽示例</h3>
<!-- 命名为header的插槽 -->
<slot name="header"></slot>
<!-- 命名为main的插槽 -->
<slot name="main"></slot>
</div>
</template>
vue
xml
<!-- App.vue(父组件) -->
<template>
<SlotsAttr>
<!-- 用v-slot:header指定内容注入到header插槽 -->
<template v-slot:header>
<h4>这是头部插槽的内容</h4>
</template>
<!-- 用#main指定内容注入到main插槽(#是v-slot的简写) -->
<template #main>
<p>这是主体插槽的内容</p>
</template>
</SlotsAttr>
</template>
<script>
import SlotsAttr from "./components/SlotsAttr.vue"
export default {
components: { SlotsAttr }
}
</script>
3. 作用域插槽:子组件向父组件 "反向传值" 的插槽
子组件在插槽中绑定数据(slotProps),父组件可以 "接收并使用" 这些数据,实现 "子传父式的插槽内容定制"。
示例:子组件SlotsAttr向父组件传递msg和demo数据
vue
xml
<!-- SlotsAttr.vue(子组件) -->
<template>
<div>
<h3>作用域插槽示例</h3>
<!-- 插槽中绑定数据(msg和demo) -->
<slot name="header" :msg="slotMsg" :demo="slotDemo"></slot>
<slot name="main" :msg="slotMsg" :demo="slotDemo"></slot>
</div>
</template>
<script>
export default {
data() {
return {
slotMsg: "子组件传递的消息",
slotDemo: "子组件传递的演示数据"
}
}
}
</script>
vue
xml
<!-- App.vue(父组件) -->
<template>
<SlotsAttr>
<!-- 接收子组件传递的slotProps,并使用其中的msg和demo -->
<template #header="slotProps">
<h4>头部插槽:{{ slotProps.msg }} - {{ slotProps.demo }}</h4>
</template>
<template #main="slotProps">
<p>主体插槽:{{ slotProps.msg }} - {{ slotProps.demo }}</p>
</template>
</SlotsAttr>
</template>
<script>
import SlotsAttr from "./components/SlotsAttr.vue"
export default {
components: { SlotsAttr }
}
</script>
渲染作用域: 插槽内容是在父组件的作用域 中定义的,所以可以直接访问父组件的数据;而插槽的slotProps是子组件传递的,父组件通过v-slot接收后才能使用。
三、总结:组件通信与插槽核心要点
| 知识点 | 核心作用 | 关键规则 / 技巧 |
|---|---|---|
| Props(父传子) | 父组件向子组件传递数据 | 子组件只能读不能直接改;支持类型、默认值、必填校验;数组 / 对象默认值需用工厂函数 |
| 自定义事件(子传父) | 子组件向父组件传递数据 / 事件 | 子组件用this.$emit("事件名", 数据)触发;父组件用@事件名监听 |
| 默认插槽 | 父组件向子组件注入单个自定义内容 | 子组件用<slot></slot>预留坑位;父组件在子组件标签内写内容即可注入 |
| 具名插槽 | 父组件向子组件注入多个命名的自定义内容 | 子组件用name给插槽命名;父组件用v-slot:名称(或#名称)指定注入位置 |
| 作用域插槽 | 子组件向父组件传递数据,父组件定制插槽内容 | 子组件在插槽中绑定数据(slotProps);父组件用v-slot="slotProps"接收并使用 |
掌握这些知识点后,Vue 组件的复用性和灵活性会大幅提升~ 后续再深入学习 Vuex、Pinia 等状态管理工具,就能应对更复杂的组件通信场景了。