最近在做公司业务,在封装一个公用的弹框,就用到了插槽,但是插槽好久没用了,就忘记了它是怎么用的,写一篇笔记来记录一下它的用法。

插槽是什么

一个公共组件为了满足不同业务场景下的业务需求,可以使用插槽最大化的实现组件公用。可以理解为“占坑”,当我们需要使用的时候去“补坑”。

插槽内容、后备(默认)内容

先来看看基本使用:

child.vue 子组件

  1. <template>
  2. <div>
  3. <slot></slot>
  4. </div>
  5. </template>

index.vue 父组件

  1. <template>
  2. <div class="home">
  3. <child>
  4. 这里可以是文本
  5. <div>也可以是任意的html</div>
  6. <!-- 也可以是其他的组件 -->
  7. <other-components />
  8. <!-- 这里可以使用当前组件的 data 数据 -->
  9. <div>{{message}}</div>
  10. </child>
  11. </div>
  12. </template>

渲染后:

  1. <div data-v-078753dd class="home">
  2. <div>
  3. 这里可以是文本
  4. <div>也可以是任意的html</div>
  5. <div data-v-078753sd>也可以是其他的组件</div>
  6. </div>
  7. </div>

也可以设置一个默认的内容,简称后备内容,或者默认内容

child.vue 子组件

  1. <template>
  2. <div>
  3. <slot>
  4. <div>默认内容</div>
  5. </slot>
  6. </div>
  7. </template>

index.vue 父组件

  1. <template>
  2. <div class="home">
  3. <child />
  4. </div>
  5. </template>

渲染后:

  1. <div data-v-078753dd class="home">
  2. <div>
  3. <div>默认内容</div>
  4. </div>
  5. </div>

具名插槽、具名插槽缩写

有时候,一个公共组件里面需要有多个“坑位”,不同的父组件调用就使用不同的“坑位”,例如对于一个带有如下模板的组件:

  1. <template>
  2. <div class="container">
  3. <header>
  4. <!-- 我们希望把页头放这里 -->
  5. </header>
  6. <main>
  7. <!-- 我们希望把主要内容放这里 -->
  8. </main>
  9. <footer>
  10. <!-- 我们希望把页脚放这里 -->
  11. </footer>
  12. </div>
  13. </template>

对于这种情况,我们可以使用具名插槽。<slot>标签有一个特殊的属性:name,这个name就是用来定义“坑位”的名字的。注意:如果不写**name**的话,会默认是一个**default**

然后,在父组件,要使用“坑位”的话,就带上指定的name。如果没有带上name,会使用默认的“坑位”。

child.vue 子组件

  1. <template>
  2. <div class="container">
  3. <header>
  4. <!-- 我们希望把页头放这里 -->
  5. <slot name="header" />
  6. </header>
  7. <main>
  8. <!-- 我们希望把主要内容放这里 -->
  9. <slot /> <!-- 等价于 <slot name="default" /> -->
  10. </main>
  11. <footer>
  12. <!-- 我们希望把页脚放这里 -->
  13. <slot name="footer" />
  14. </footer>
  15. </div>
  16. </template>

index.vue 父组件

在向具名插槽提供内容的时候,我们可以在<template>元素上使用v-slot指令,并以参数的形式提供其名称,如下:

  1. <template>
  2. <div class="home">
  3. <child>
  4. <template v-slot:header>
  5. <p>header</p>
  6. </template>
  7. <p>default</p>
  8. <template v-slot:footer>
  9. <p>footer</p>
  10. </template>
  11. </child>
  12. </div>
  13. </template>

渲染后:

  1. <div data-v-078753dd class="home">
  2. <div class="container">
  3. <header>
  4. <p>header</p>
  5. </header>
  6. <main>
  7. <p>default</p>
  8. </main>
  9. <footer>
  10. <p>footer</p>
  11. </footer>
  12. </div>
  13. </div>

**v-slot****v-on****v-bind**一样也有缩写,即把参数之前的所有内容 (**v-slot:**) 替换为字符 **#**。例如 **v-slot:header** 可以被重写为 **#header**

index.vue

  1. <template>
  2. <div class="home">
  3. <child>
  4. <template #header>
  5. <p>header</p>
  6. </template>
  7. <p>default</p>
  8. <template #footer>
  9. <p>footer</p>
  10. </template>
  11. </child>
  12. </div>
  13. </template>

作用域插槽

插槽可以访问相同实例的属性(可以理解成当前组件的属性是可以访问的到的),但是,要访问子组件的实例属性,却是访问不到的。如果要让插槽中的内容也能访问子组件的数据,可以给子组件添加绑定一个属性:

child.vue 子组件

  1. <template>
  2. <div class="container">
  3. <header>
  4. <!-- 我们希望把页头放这里 -->
  5. <!-- <slot name="header" /> -->
  6. <slot name="header" :user="user" />
  7. </header>
  8. <main>
  9. <!-- 我们希望把主要内容放这里 -->
  10. <slot /> <!-- 等价于 <slot name="default" /> -->
  11. </main>
  12. <footer>
  13. <!-- 我们希望把页脚放这里 -->
  14. <slot name="footer" />
  15. </footer>
  16. </div>
  17. </template>
  18. <script>
  19. export default {
  20. data() {
  21. return {
  22. user: {
  23. firstName: 'Fan',
  24. lastName: 'Jun'
  25. }
  26. }
  27. }
  28. }
  29. </script>

绑定在 <slot> 元素上的 attribute(属性) 被称为插槽 prop

然后在父组件中可以使用带值的 **v-slot** 来定义我们提供的插槽 prop 的名字:

  1. <template>
  2. <div class="home">
  3. <child>
  4. <!-- <template v-slot:header> -->
  5. <!-- 不简写的写法 -->
  6. <!-- <template v-slot:header="slotProps"> -->
  7. <!-- 简写的写法 -->
  8. <template #header="slotProps">
  9. <p>header</p>
  10. <p>数据:{{ slotProps }}</p>
  11. </template>
  12. <p>default</p>
  13. <template #footer>
  14. <p>footer</p>
  15. </template>
  16. </child>
  17. </div>
  18. </template>