在之前的章节中,我们已经了解到组件能够接收任意类型的 JavaScript 值作为 props,但组件要如何接收模板内容呢?在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。
举例来说,这里有一个 <FancyButton>
组件,可以像这样使用:
<FancyButton>
Click me! <!-- 插槽内容 -->
</FancyButton>
而 <FancyButton>
的模板是这样的:
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>
<slot>
元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。
最终渲染出的 DOM 是这样:
<button class="fancy-btn">Click me!</button>
插槽内容可以是任意合法的模板内容,不局限于文本。例如我们可以传入多个元素,甚至是组件:
<FancyButton>
<span style="color:red">Click me!</span>
<AwesomeIcon name="plus" />
</FancyButton>
渲染作用域
插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。举例来说:
<span>{{ message }}</span>
<FancyButton>{{ message }}</FancyButton>
这里的两个 {{ message }}
插值表达式渲染的内容都是一样的。
插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。换言之:
父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。
默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容。比如有这样一个 <SubmitButton>
组件:
<button type="submit">
<slot></slot>
</button>
如果我们想在父组件没有提供任何插槽内容时在 <button>
内渲染“Submit”,只需要将“Submit”写在 <slot>
标签之间来作为默认内容:
<button type="submit">
<slot>
Submit <!-- 默认内容 -->
</slot>
</button>
现在,当我们在父组件中使用 <SubmitButton>
且没有提供任何插槽内容时:
<SubmitButton />
“Submit”将会被作为默认内容渲染:
<button type="submit">Submit</button>
但如果我们提供了插槽内容:
<SubmitButton>Save</SubmitButton>
那么被显式提供的内容会取代默认内容:
<button type="submit">Save</button>
基本用法:
-
具名插槽(Named Slots):可以在父组件中使用
<template v-slot:name>
或者简写为<template #name>
来定义具名插槽,子组件中使用<slot name="name">
来接收。v-slot
有对应的简写#
,因此<template v-slot:header>
可以简写为<template #header>
。其意思就是“将这部分模板片段传入子组件的 header 插槽中”。<!-- ParentComponent.vue --> <template> <ChildComponent> <template v-slot:header> <h1>这是头部内容</h1> </template> <template v-slot:footer> <h2>这是底部内容</h2> </template> </ChildComponent> </template>
<!-- ChildComponent.vue --> <template> <div> <slot name="header"></slot> <slot name="footer"></slot> </div> </template>
-
作用域插槽(Scoped Slots):允许子组件向父组件传递数据。在上面的渲染作用域中我们讨论到,插槽的内容无法访问到子组件的状态。然而在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。我们也确实有办法这么做!可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes:
<!-- ParentComponent.vue --> <template> <ChildComponent v-slot="{ message }"> <p>父组件收到的消息:{{ message }}</p> </ChildComponent> </template>
当需要接收插槽 props 时,默认插槽和具名插槽的使用方式有一些小区别。下面我们将先展示默认插槽如何接受 props,通过子组件标签上的 v-slot
指令,直接接收到了一个插槽 props 对象:
<!-- ChildComponent.vue -->
<template>
<div>
<slot :message="msg"></slot>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'Hello from ChildComponent!'
};
}
}
</script>
动态插槽名
动态指令参数在 v-slot
上也是有效的,即可以定义下面这样的动态插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
<!-- 缩写为 -->
<template #[dynamicSlotName]>
...
</template>
</base-layout>
总的来说,插槽是Vue中非常重要和灵活的特性,可以用来构建更加动态和可复用的组件。
-
插槽的作用:允许父组件向子组件传递内容或数据,增强了组件的灵活性和复用性。
- 具名插槽 vs 作用域插槽:具名插槽用于在子组件中接收固定命名的内容,而作用域插槽则允许子组件将数据传递给父组件。
- 插槽的默认内容:如果父组件没有传递内容给插槽,子组件可以在插槽标签内定义默认内容,这样可以确保组件的正常显示。
- 动态插槽名:插槽名可以是动态的,这在需要根据条件渲染不同内容时非常有用。