3.3 slot 挖坑填坑的艺术

5 阅读4分钟

核心隐喻:

  1. 挖坑: 组件留个洞。
  2. 填坑: 父组件往里塞东西。

小题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>

结果: 上面显示"标题",下面显示"内容",组成一张卡片。

傻瓜口令:组合起来能做大卡片。