Vue3 Slot

组件可以在 template 当中设置一个 slot 的 tag,用于在使用该组件时,传入一些内容来替换掉 slot

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<FancyButton>
  Click me! <!-- slot content -->
</FancyButton>

<button class="fancy-btn">
  <slot></slot> <!-- slot outlet -->
</button>

// final output
<button class="fancy-btn">Click me!</button>

传给 slot 的内容可以是文本,也可以是 html element

scope

slot 的渲染 scope 限于父组件,当传入一个变量,这个变量只能访问父组件,而不能访问子组件的变量

Expressions in the parent template only have access to the parent scope; expressions in the child template only have access to the child scope.

有些情况下 slot 的内容需要根据组件本身的变量来设置。这种情况可以像传递 props 给组件一样传递 attributes 给组件。这种时候 slot 的内容就可以根据子组件的变量来获取。

1
2
3
4
5
6
7
8
<!-- <MyComponent> template -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>

本质上可以当作是传递了一个函数下去,由组件调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
MyComponent({
  // passing the default slot, but as a function
  default: (slotProps) => {
    return `${slotProps.text} ${slotProps.count}`
  }
})

function MyComponent(slots) {
  const greetingMessage = 'hello'
  return `<div>${
    // call the slot function with props!
    slots.default({ text: greetingMessage, count: 1 })
  }</div>`
}

fallback content

组件可以为 slot 设置默认值

1
2
3
4
5
<button type="submit">
  <slot>
    Submit <!-- fallback content -->
  </slot>
</button>

named slot

如果一个组件中有多个 slot,可以通过 name 属性来进行区分。使用时可以通过属性 v-slot:slotName 或者缩写 #slotName 明确是传给哪一个 slot,不填时默认是 default

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

<BaseLayout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

默认顶级的元素都会传递给 default slot

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <!-- implicit default slot -->
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

动态 slot name

通过中括号可以使用动态变量或表达式作为 slot name

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- with shorthand -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>