在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的特性。
<slot>元素作为承载分发内容的出口,可以理解为一个占位符,或者说是子组件暴露的一个让父组件传入自定义内容的接口。
插槽内可以包含任何模板代码,包括 HTML,甚至其他的组件。
slot有三种类型
- 默认插槽
default
- 具名插槽
name
- 作用域插槽
v-slot
在子组件中:
- 插槽用
<slot>
标签来确定渲染的位置,里面放如果父组件没传内容时的后备内容。一个不带name
的<slot>
出口会带有隐含的名字“default
”。 - 具名插槽用
name
属性来表示插槽的名字 - 作用域插槽在作用域上绑定属性来将子组件的信息传给父组件使用
// Child.vue <template> <div> <main> <!-- 默认插槽 --> <slot> <!-- slot内为后备内容 --> <h3>没传内容</h3> </slot> </main> <!-- 具名插槽 --> <header> <slot name="header"> <h3>没传header插槽</h3> </slot> </header> <!-- 作用域插槽 --> <footer> <slot name="footer" testProps="子组件的值"> <h3>没传footer插槽</h3> </slot> <footer> </div> </template>
v-slot
- 具名插槽通过指令参数v-slot:插槽名的形式传入,可以简化为 #插槽名
- 作用域插槽通过v-slot:xxx="slotProps"的slotProps来获取子组件传出的属性
- v-slot属性只能在<template>上使用,但在【只有默认插槽时】可以在组件标签上使用
//Parent.vue <template> <child> <!--默认插槽--> <template v-slot> // v-slot:default <div>默认插槽</div> </template> <!--具名插槽--> <template #header> // v-slot:header <div>具名插槽</div> </template> <!--作用域插槽--> <template #footer="slotProps"> //v-slot:footer <div> {{slotProps.testProps}} </div> </template> <child> </template>
拓展用法:
- 同样可以通过解构获取
v-slot={user}
,
还可以重命名v-slot="{user: newName}"
和定义默认值v-slot="{user = '默认值'}"
- 插槽名可以是动态变化的
v-slot:[slotName]
注意
- 默认插槽名为default,可以省略default直接写v-slot。缩写为#时不能不写参数,写成#default(这点所有指令都一样,v-bind、v-on)
- 多个插槽混用时,v-slot不能省略default
- 只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template> 的语法
示例
插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。这在设计封装数据逻辑同时允许父级组件自定义部分布局的可复用组件时是最有用的。
例如,我们要实现一个 <todo-list> 组件,它是一个列表且包含布局和过滤逻辑:
<ul> <li v-for="todo in filteredTodos" v-bind:key="todo.id" > {{ todo.text }} </li> </ul>
我们可以将每个 todo 作为父级组件的插槽,以此通过父级组件对其进行控制,然后将 todo 作为一个插槽 prop 进行绑定:
<ul> <li v-for="todo in filteredTodos" v-bind:key="todo.id" > <!-- 我们为每个 todo 准备了一个插槽, 将 `todo` 对象作为一个插槽的 prop 传入。 --> <slot name="todo" v-bind:todo="todo"> <!-- 后备内容 --> {{ todo.text }} </slot> </li> </ul>
现在当我们使用 <todo-list> 组件的时候,我们可以选择为 todo 定义一个不一样的 <template> 作为替代方案,并且可以从子组件获取数据:
<todo-list v-bind:todos="todos"> <template v-slot:todo="{ todo }"> <span v-if="todo.isComplete">✓</span> {{ todo.text }} </template> </todo-list>
这里用到了解构插槽,v-slot:todo="{ todo }"
作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里:
function (slotProps) { // 插槽内容 }
这意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。