Vue3 插槽
插槽内容
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。
1 | <todo-button> |
1 | <!-- todo-button 组件模板 --> |
当组件渲染的时候,<slot></slot>
将会被替换为“Add todo”
1 | <!-- 渲染 HTML --> |
插槽还可以包含任何模板代码,包括 HTML或其他组件
1 | <todo-button> |
TIP
如果
<todo-button>
的 template 中没有包含一个<slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
渲染作用域
当你想在一个插槽中使用数据时,例如
1 | <todo-button> |
该插槽可以访问与模板其余部分相同的实例 property (即相同的“作用域”)
插槽不能访问 <todo-button>
的作用域。
1 | <todo-button action="delete"> |
TIP
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
备用内容
有时为一个插槽指定备用 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
1 | <button type="submit"> |
为了将“Submit”作为备用内容,我们可以将它放在 <slot>
标签内:
1 | <button type="submit"> |
当我们在一个父级组件中使用 <submit-button>
并且不提供任何插槽内容时,备用内容“Submit”将会被渲染:
1 | <submit-button></submit-button> |
1 | <button type="submit"> |
如果我们提供内容,则这个提供的内容将会被渲染从而取代备用内容:
1 | <submit-button> |
1 | <button type="submit"> |
具名插槽
有时我们需要多个插槽,对于这样的情况,<slot>
元素有一个特殊的 attribute:name
。通过它可以为不同的插槽分配独立的 ID,也就能够以此来决定内容应该渲染到什么地方:
1 | <div class="container"> |
一个不带 name
的 <slot>
出口会带有隐含的名字“default”
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
1 | <base-layout> |
渲染的 HTML 将会是:
1 | <div class="container"> |
注意,**v-slot
只能添加在 <template>
上**
作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的。当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式。
例如:
1 | app.component('todo-list', { |
我们可能会想把 {{ item }}
替换为 <slot>
,以便在父组件上对其自定义。
1 | <todo-list> |
但是,这是行不通的,因为只有在 <todo-list>
组件中可以访问 item
,且插槽内容是在它的父组件上提供的。
要使 item
在父级提供的插槽内容上可用,我们可以添加一个 <slot>
元素并将其作为一个 attribute 绑定:
1 | <ul> |
绑定在 <slot>
元素上的 attribute 被称为插槽 prop。现在,在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop 的名字:
1 | <todo-list> |
独占默认插槽的缩写语法
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上
1 | <todo-list v-slot:default="slotProps"> |
就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot
被假定对应默认插槽:
1 | <todo-list v-slot="slotProps"> |
默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
1 | <!-- 无效,会导致警告 --> |
只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template>
的语法
1 | <todo-list> |
解构插槽 Prop
作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里
1 | function (slotProps) { |
这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。因此你也可以使用 ES2015 解构 来传入具体的插槽 prop,如下:
1 | <todo-list v-slot="{ item }"> |
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 item
重命名为 todo
:
1 | <todo-list v-slot="{ item: todo }"> |
你甚至可以定义备用内容,用于插槽 prop 是 undefined 的情形:
1 | <todo-list v-slot="{ item = 'Placeholder' }"> |
动态插槽名
动态指令参数也可以用在 v-slot
上,来定义动态的插槽名
1 | <base-layout> |
具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
1 | <base-layout> |
和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
1 | <!-- 这将触发一个警告 --> |
如果希望使用缩写的话,你必须始终以明确的插槽名取而代之
1 | <todo-list #default="{ item }"> |