核心隐喻:
- 挖坑: 组件留个洞。
- 填坑: 父组件往里塞东西。
小题1:基础挖坑
目标:理解最简单的不传参插槽。
// Child.vue
<template>
<div class="box">
<!-- 组件这里挖个坑 -->
<slot></slot>
</div>
</template>
<style>
.box { border: 1px solid black; padding: 10px; }
</style>
结果: 屏幕上出现一个带黑框的空盒子。
傻瓜口令:组件写slot挖个坑。
小题2:基础填坑
目标:验证父组件内容替换插槽。
// Parent.vue
<script setup>
import Child from './Child.vue'
</script>
<template>
<Child>
Hello
</Child>
</template>
结果: 黑框盒子里出现了 "Hello"。
傻瓜口令:标签中间写字填坑。
小题3:传入HTML
目标:破除"只能传字"的误解。
// Parent.vue
<script setup>
import Child from './Child.vue'
</script>
<template>
<Child>
<button>点我</button>
</Child>
</template>
结果: 黑框盒子里出现了一个按钮。
傻瓜口令:标签也能往坑里填。
小题4:默认显示
目标:理解 fallback content。
// Child.vue
<template>
<slot>
暂无内容
</slot>
</template>
// Parent.vue (如果不传东西)
<template><Child></Child></template>
结果: 盒子里显示 "暂无内容"。
傻瓜口令:slot中间写字做备胎。
小题5:覆盖默认
目标:理解覆盖机制。
// Parent.vue
<template>
<Child>
有东西了
</Child>
</template>
结果: 显示 "有东西了" ("暂无内容" 不见了)。
傻瓜口令:填了新坑备胎消失。
小题6:具名起名
目标:开启多插槽模式。
// Child.vue
<template>
<slot name="header"></slot>
<slot name="footer"></slot>
</template>
结果: 两个代码里写的坑,一个叫header,一个叫footer。
傻瓜口令:加name属性给坑起名。
小题7:具名指定
目标:学习 # 语法定向投送。
// Parent.vue
<script setup>
import Child from './Child.vue'
</script>
<template>
<Child>
<template #header>我是头</template>
<template #footer>我是脚</template>
</Child>
</template>
结果: 上面显示 "我是头",下面显示 "我是脚"。
傻瓜口令:井号加名字精准填坑。
小题8:位置无关性
目标:证明渲染位置由子组件决定。
// Parent.vue (故意反着写)
<template>
<Child>
<template #footer>我是脚</template>
<template #header>我是头</template>
</Child>
</template>
结果: 依然是 "我是头" 在上面,"我是脚" 在下面。
傻瓜口令:填坑顺序随便写。
小题9:隐式默认
目标:理解未命名内容的去向。
// Child.vue
<template>
<slot></slot> <!-- 等同于 name="default" -->
</template>
// Parent.vue
<template>
<Child>
<template #default>我去了默认坑</template>
</Child>
</template>
结果: 显示 "我去了默认坑"。
傻瓜口令:没名字自动进default坑。
小题10:一坑多填 (复用)
目标:理解插槽复用。
// Child.vue
<template>
<slot></slot>
<hr>
<slot></slot>
</template>
// Parent.vue
<template>
<Child>Hi</Child>
</template>
结果: 屏幕显示两个 "Hi",中间有条线。
傻瓜口令:这头填坑那头出两份。
小题11:数据穿越 (作用域-发)
目标:ATM机吐钱。
// Child.vue
<script setup>
const count = 100
</script>
<template>
<slot :num="count"></slot>
</template>
结果: 页面显示空白(还没人接苹果)。
傻瓜口令:冒号绑定把数传出去。
小题12:数据接收 (作用域-收)
目标:用袋子接钱。
// Parent.vue
<script setup>
import Child from './Child.vue'
</script>
<template>
<Child>
<template #default="{ num }">
收到了:{{ num }}
</template>
</Child>
</template>
结果: 屏幕显示 "收到了:100"。
傻瓜口令:井号赋值大括号接住。
小题13:解构重命名
目标:解决变量名冲突。
// Parent.vue
<template>
<Child>
<template #default="{ num: myNum }">
我的数:{{ myNum }}
</template>
</Child>
</template>
结果: 显示 "我的数:100"。
傻瓜口令:冒号后面起个新名。
小题14:传递对象
目标:批量传递数据。
// Child.vue
<script setup>
const user = {name:'Tom', age:10}
</script>
<template>
<slot :user="user"></slot>
</template>
// Parent.vue
<template>
<Child>
<template #default="{ user }">
{{ user.name }}
</template>
</Child>
</template>
结果: 显示 "Tom"。
傻瓜口令:传个对象也能接得住。
小题15:简写强制
目标:巩固记忆 # 是唯一真神。
// 推荐
<template #header>
// 禁止
<template v-slot:header>
结果: 只用井号,代码更短。
傻瓜口令:只用井号别用v-slot。
小题16:样式归属 (父)
目标:谁生谁养。
// Parent.vue
<template>
<Child>
<span style="color:red">红字</span>
</Child>
</template>
结果: 显示红色的 "红字"。
傻瓜口令:自己填的字自己变色。
小题17:样式隔离 (子)
目标:验证子组件普通样式不影响插槽。
// Child.vue
<style scoped>
span { color: green; } /* 这里的样式影响不到插槽内容 */
</style>
结果: "红字" 依然是红色,不会变绿。
傻瓜口令:组件管不到填坑的色。
小题18:事件传递
目标:交互逻辑归属。
// Parent.vue
<script setup>
const add = () => alert('疼')
</script>
<template>
<Child>
<button @click="add">打我</button>
</Child>
</template>
结果: 点击按钮,弹出 "疼"(父组件响应)。
傻瓜口令:填的按钮听父组件的。
小题19:响应式联动
目标:动态填坑。
// Parent.vue
<script setup>
import { ref } from 'vue'
const text = ref('早')
</script>
<template>
<Child>{{ text }}</Child>
<button @click="text='晚'">改</button>
</template>
结果: 点"改"按钮,坑里的 "早" 变成了 "晚"。
傻瓜口令:父组件变了坑里也变。
小题20:综合战役 - 卡片组件
目标:组合拳。
// Card.vue
<template>
<div class="card">
<div class="head"><slot name="header"></slot></div>
<div class="body"><slot></slot></div>
</div>
</template>
// Parent.vue
<template>
<Card>
<template #header>标题</template>
内容
</Card>
</template>
结果: 上面显示"标题",下面显示"内容",组成一张卡片。
傻瓜口令:组合起来能做大卡片。