插槽(slot)可以理解为对 Vue 组件的扩展,我们可以通过插槽向组件内部的指定位置传递内容,也可以实现父子组件传参。

简单的插槽

组件 NavigationLink 定义如下:

  1. <template>
  2. <a :href="url">
  3. <slot></slot>
  4. </a>
  5. </template>
  6. <script>
  7. export default {
  8. name: "NavigationLink",
  9. props: ['url']
  10. }
  11. </script>

使用该组件时,在标签对之间输入一些文字,如下:

  1. <NavigationLink url="/home">首页</NavigationLink>

当组件渲染的时候,<slot></slot> 将会被替换为“首页”。如果组件 NavigationLink 中没有定义插槽,则该组件标签对之间的任何内容都会被抛弃。

也可以为插槽设置默认内容,它会在标签对之间的内容为空时被渲染。例如在 SubmitButton 组件中,希望它默认显示文本“Submit”,可以指定插槽的默认内容。

  1. <template>
  2. <button type="submit">
  3. <slot>Submit</slot>
  4. </button>
  5. </template>
  6. <script>
  7. export default {
  8. name: "SubmitButton"
  9. }
  10. </script>

具名插槽

有时我们需要在一个组件中设置多个插槽,例如下面这样的组件:

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

对于这样的情况,可以给插槽 <slot> 指定一个 name 属性,该属性用来匹配有对应特征的元素。

  1. <div class="container">
  2. <header>
  3. <slot name="header"></slot>
  4. </header>
  5. <main>
  6. <slot>未显示给出 name 属性,默认其值为 "default"</slot>
  7. </main>
  8. <footer>
  9. <slot name="footer"></slot>
  10. </footer>
  11. </div>

我们可以在 <template> 标签上使用 v-slot 指令,以下面的形式为其指定名称。

  1. <base-layout>
  2. <template v-slot:header>
  3. <h1>Here might be a page title</h1>
  4. </template>
  5. <template>
  6. <p>A paragraph for the main content.</p>
  7. <p>未指定 "v-slot",将其视为 "default"</p>
  8. </template>
  9. <template v-slot:footer>
  10. <p>Here's some contact info</p>
  11. </template>
  12. </base-layout>

v-onv-bind 一样,v-slot: 可简化为 #,比如 v-slot:header 可简写为 #header

  1. <base-layout>
  2. <template #header>
  3. <h1>Here might be a page title</h1>
  4. </template>
  5. <p>A paragraph for the main content.</p>
  6. <p>不指定 v-slot 时,可省略外层的 <template> 标签</p>
  7. <template #footer>
  8. <p>Here's some contact info</p>
  9. </template>
  10. </base-layout>

插槽的 props

我们可以将插槽看作一个组件,它也有自己的 props。例如下面的组件 ShowText ,它默认显示 userlastName,而父组件希望它显示 userfirstName

  1. <template>
  2. <p>
  3. <slot v-bind:user="user">
  4. {{user.lastName}}
  5. </slot>
  6. </p>
  7. </template>
  8. <script>
  9. export default {
  10. name: "ShowText",
  11. data() {
  12. return {
  13. user: {
  14. firstName: "Tom",
  15. lastName: "Smith"
  16. }
  17. }
  18. }
  19. }
  20. </script>

给插槽的 props 绑定一个 user,父组件以以下形式令其显示 userfirstName

  1. <ShowText>
  2. <template v-slot:default="slotProps">
  3. {{slotProps.user.firstName}}
  4. </template>
  5. </ShowText>

上例中,slotProps 是给插槽的 props 定义的名字,方便父组件访问其 props。

也可以使用 ES2015 的解构特性来传入具体的插槽 prop,如下:

  1. <ShowText>
  2. <template v-slot:default="{user}">
  3. {{user.firstName}}
  4. </template>
  5. </ShowText>

可以为某个 prop 重命名,例如将 user 重命名为 person

  1. <ShowText>
  2. <template v-slot:default="{user: person}">
  3. {{person.firstName}}
  4. </template>
  5. </ShowText>

甚至可以定义其默认内容,用于插槽 props 是 undefined 的情形,如下:

  1. <ShowText>
  2. <template v-slot:default="{user = {firstName: 'Guest'}}">
  3. {{user.firstName}}
  4. </template>
  5. </ShowText>

动态的插槽名

以如下的形式定义动态的插槽名。

  1. <base-layout>
  2. <template v-slot:[dynamicSlotName]>
  3. ...
  4. </template>
  5. </base-layout>