Vue3$View-Slots

125 阅读1分钟

Vue3$View-Slots

0. Slots in Big Picture

我们知道,在 Vue 中,数据 data 驱动着页面 view。之前我们已经讨论了 data,现在讨论 view。

在 Vue 中,我们使用 template 来表示 view,之前的 Vue3$View-Template 里讨论了 template 的基本语法。

除了可以定义自己的 template,组件也可以接收外部的 template。而这个预留的空间,就是slot。

props 和 slot 的理解:

  • props 接收的是数据(虽然也可以是函数)
  • slot 接收的是函数(函数可以返回各种类型,也就可以返回 template)

1. Slot 的基本使用 Slot Content and Outlet

// @file FancyButton.vue
<button class="fancy-btn">
  <slot></slot> <!-- slot outlet -->
</button>
<FancyButton>
  Click me! <!-- slot content -->
</FancyButton>

slots.png

1.1 Fallback Content

<button type="submit">
  <slot>
    Submit <!-- fallback content -->
  </slot>
</button>

2. Named Slots

当有多个 slots 时,可以给每个 slot 命名(name),没有命名的名字为 default。

named-slots.png

// @file BaseLayout.vue -- producer
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
// consumer
<BaseLayout>
  <template #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>

2.1 Dynamic Slot Names

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

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

3. Conditional Slots

<template>
  <div class="card">
    <div v-if="$slots.header" class="card-header">
      <slot name="header" />
    </div>
    
    <div v-if="$slots.default" class="card-content">
      <slot />
    </div>
    
    <div v-if="$slots.footer" class="card-footer">
      <slot name="footer" />
    </div>
  </div>
</template>

4. Scoped Slots

默认情况下,consumer 传给 slot 的内容只能包含自身的数据。如果 consumer 想在 slot 中使用 producer 自身的数据,可以使用 scoped slots。即接收 slot 的数据。

两种情况:默认的 slot 和 命名的 slot。

4.1 Default Scoped Slot

scoped-slots.svg

4.2 Named Scope Slot

// @file MyComponent -- producer
<slot name="header" message="hello"></slot>
// name is a reserved, so not passed.
// consumer
<MyComponent>
  <template #header="headerProps">
    {{ headerProps }}
  </template>

  <template #default="defaultProps">
    {{ defaultProps }}
  </template>

  <template #footer="footerProps">
    {{ footerProps }}
  </template>
</MyComponent>