# slot 模式

每个组件实例都拥有独立的 Shadow 树,代表组件自身模板中的节点结构。当组件的使用者在组件标签内放置子节点时,这些子节点会被视为 slot 内容,放置到 <slot> 的位置上。换句话说,<slot> 是一个占位符,代表 slot 内容所在的位置。

自定义组件使用 <slot> 时,slot 有三种工作模式可选。每个组件都可以任选其一。

  • 单一 slot 是默认情况,仅支持一个 <slot> 节点;
  • 多 slot 支持多个 <slot>,每个 slot 需要有一个名字 <slot name="xxx">
  • 动态 slot 支持在 wx:for 内部使用 <slot>,同时可以向 slot 内容节点树传递数据。

# 单一 slot

默认情况下激活的是单一 slot 模式。这种模式具有最优的性能。

在这个模式下,自定义组件 WXML 中只能包含一个 <slot> 节点。例如:

<!-- custom-component.wxml -->
<view>
  <slot />
</view>

使用这个自定义组件时,传入内容节点:

<custom-component>
  <view> content </view>
</custom-component>

<view> content </view> 最终会被放置到 <slot> 的位置上。

如果在自定义组件 WXML 内写了多个 <slot> 节点,只有第一个生效。

# 多 slot

在组件 options 中声明 multipleSlots: true 可激活多 slot 模式。

// Definition 形式的 custom-component.json
export default Component({
  options: {
    multipleSlots: true,
  },
})
// Chaining 形式的 custom-component.json
export default Component()
  .options({ multipleSlots: true })
  .register()

多 slot 模式允许自定义组件 WXML 中包含多个 <slot> 节点。每个 <slot> 都必须有名字 name,其值必须是一个唯一的字符串。例如:

<!-- custom-component.wxml -->
<view class="foo-wrapper">
  <slot name="foo" />
</view>
<view class="bar-wrapper">
  <slot name="bar" />
</view>

在使用这个自定义组件时,需要为每个 slot 内容节点指定它所在的 slot 名字,例如:

<custom-component>
  <!-- 这个 <view> 会放入 <slot name="foo"> 中 -->
  <view slot="foo"> foo content </view>

  <!-- 可以用 block 来避免额外生成一个 <view> -->
  <block slot="bar"> bar content </block>
</custom-component>

# 静态 slot 的 slot 内容生命周期

单一 slot 和多 slot 模式统一称为静态 slot 模式。

在静态 slot 下,即使自定义组件没有 <slot> 节点,slot 内容节点依然会被创建并触发 attached detached 等生命周期。例如,如果在自定义组件里面这么写:

<!-- custom-component.wxml -->
<view wx:if="{{ cond }}">
  <slot />
</view>

而在页面内这样写:

<custom-component>
  <view> content </view>
  <another-custom-component />
</custom-component>

如果 cond 为假,<slot> 节点就不存在,但是:

  • 即使 <slot> 节点不存在,<view> content </view><another-custom-component> 仍然会被创建,<another-custom-component>attached 生命周期依然会被触发;
  • 如果后续 cond 变成真,<slot> 节点会被创建,但是 <view> content </view><another-custom-component> 并不会被重新创建,<another-custom-component>attached 生命周期也不会再次触发。

如果想要 slot 内容与 <slot> 节点一起创建或销毁,可以使用动态 slot 模式。

# 动态 slot

动态 slot 模式允许自定义组件使用若干个 <slot> 节点。

在组件 options 中声明 dynamicSlots: true 可激活动态 slot 模式。

// Definition 形式的 custom-component.json
export default Component({
  options: {
    dynamicSlots: true,
  },
})
// Chaining 形式的 custom-component.json
export default Component()
  .options({ dynamicSlots: true })
  .register()

在使用动态 slot 时,每个 <slot> 拥有 slot 内容节点树的一份拷贝(这份拷贝会随着 <slot> 一同创建和销毁)。例如对于组件:

<!-- custom-component.wxml -->
<view class="foo">
  <slot />
</view>
<view class="bar">
  <slot />
</view>

在页面内这样写:

<custom-component>
  <view> content </view>
</custom-component>

将会最终出现两个 <view> content </view>,分别位于 <view class="foo"><view class="bar"> 内部。

同时,<slot> 节点可以传递数据字段,让 slot 内容节点树使用。这使得动态 slot 非常适合 <slot> 节点出现在 wx:for 列表内部的情况。例如对于组件:

<!-- custom-component.wxml -->
<view wx:for="{{ list }}">
  <slot list-index="{{ index }}" item="{{ item }}" />
</view>

通过 slot: 语法,在页面内可以接收到 list-indexitem

<custom-component>
  <view slot:item> {{ item }} </view>
</custom-component>

使用 slot: 接收数据时,可以用 = 来指定一个别名。此外,还可以使用 <block> 来避免生成额外的节点:

<custom-component>
  <block slot:listIndex="{{ i }}"> {{ i }} </block>
  <block slot:item> {{ item }} </block>
</custom-component>

动态 slot 模式同样支持类似多 slot 的 name 属性,不过一般并不常用。