Vue插槽的主要作用是承载分发内容的出口,分为:匿名插槽、具名插槽以及作用域插槽3种
举个"栗子",创建以下文件:
-
child.vue:封装 name 为 ChildExample 的组件
-
parent.vue:引入 child.vue,调用 ChildExample 组件
匿名插槽
<slot> </slot>
是最常用的插槽,根据需要自定义内容
// child.vue
<slot></slot>
// parent.vue
<child-example>
<span>这是匿名插槽的内容</span>
</child-example>
具名插槽
假设一个插槽满足不了需求,而多个插槽的设置,赋上 name
值是必要的喔~
注意:匿名插槽是特殊的具名插槽,其 name 为
default
,避免同名!
<template> + v-slot指令
(2.6.0+),或<slot> + name属性
// child.vue
<slot name="left"></slot>
<slot name="right"></slot>
// parent.vue
<child-example>
<span slot="left">这是具名插槽left的内容</span>
<!-- 2.6.0 + -->
<template v-slot:right>
<span>这是具名插槽right的内容</span>
</template>
</child-example>
作用域插槽
这个插槽可以访问到子组件
中的数据,为内容分发实现更多可能性~ 推荐v-slot:slotName="slotProps"
的写法,替代了slot + slot-scope
// child.vue
<slot name="demo" :data="list" :param="option"></slot>
// parent.vue
<child-example>
<!-- 2.6.0 + -->
<template v-slot:demo="{data, param }">
<span>{{data}}{{param}}</span>
</template>
<!-- 或slot+slot-scope -->
<!-- <span slot="demo" slot-scope="someProps">{{someProps.data}}{{someProps.param}}</span> -->
</child-example>
⚠️ 请注意
-
slot 和 slot-scope 在
Vue 2.x
版本仍被支持,但已被官方废弃且不出现在Vue 3
,推荐使用v-slot指令 -
v-slot 指令只能加在template 或 自定义组件 上
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用,即可把 v-slot 直接用在组件上(独占默认插槽的缩写语法)
-
插槽 name 一致的 v-slot 会 覆盖 slot
-
插槽 name 一致的插槽内容,后者覆盖前者
-
插槽编译作用域: 编译作用域
-
父级模板里的所有内容都是在父级作用域中编译的;
-
子模板里的所有内容都是在子作用域中编译的
-
$slots
& $scopedSlots
与插槽相关的两个API,可知官方说明概况如下:
使用$slots
和$scopedSlots
封装组件
封装一个待办事项组件:在todoList.vue封装,调用在demo.vue
方法一
暂不利用插槽,todoList.vue如下:
<template>
<ul>
<li v-for="(item, index) in list" :key="index">
<span>{{ item.date }} :</span>
{{ item.thing }}
</li>
</ul>
</template>
<script>
export default {
name: "todoList",
props: {
list: {
type: Array,
default() {
return [];
}
}
}
};
</script>
在demo.vue引入todoList.vue,并调用:
<template>
<div id="demo">
<todo-list :list="data"></todo-list>
</div>
</template>
import todoList from "src/components/todoList";
export default {
name: "Demo",
components: {todoList},
data() {
return {
data: [
{ date: "Monday", thing: "Working" },
{ date: "Tuesday", thing: "Working" },
{ date: "Wednesday", thing: "Working" },
{ date: "Thursday", thing: "Working" },
{ date: "Friday", thing: "Working" },
{ date: "Saturday", thing: "Vacation" },
{ date: "Sunday", thing: "Vacation" }
],
};
}
};
效果-->
方法二
换一种实现方法:访问$slots
(已知:访问$slots
有利于使用渲染函数
编写组件)
根据数据,可知需要设置7个插槽,其name对应取date值。在此,补充一个知识点:
v-slot 支持 动态插槽名,对应语法:
v-slot:[dynamicSlotName]
所以,todoList.vue改为:
<template>
<todoItem></todoItem>
</template>
<script>
export default {
name: "todoList",
components: {
todoItem: {
render(h) {
let root = this.$parent;
const {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday} = root.$slots;
return h("ul", [
h("li", Monday),
h("li", Tuesday),
h("li", Wednesday),
h("li", Thursday),
h("li", Friday),
h("li", Saturday),
h("li", Sunday)
]);
}
}
}
};
</script>
对应demo.vue调用改为:
<todo-list>
<template v-for="(item, index) in data" v-slot:[item.date]>
<p :key="index">
<span>{{ item.date }} --- </span>
<span style="color:#aaa">{{ item.thing }}</span>
</p>
</template>
</todo-list>
效果-->
方法三
实际上,方法二这样编写并不常见,更多的场景,是像Element-Tree组件,根据需要,既可使用默认树节点样式,也可自定义
节点内容。
待办事项组件也想如此,假设待办数据中周末两日需自定义内容,可以使用作用域插槽
来实现效果:
改变数据,用 slotName 属性指定目标插槽名称
{ date: "Saturday", thing: "Vacation", slotName: "weekend" },
{ date: "Sunday", thing: "Vacation", slotName: "weekend" }
相对于方法一,demo.vue添加编写目标插槽(weekend)
<todo-list :list="data2">
<template v-slot:weekend="item">
<p style="color:#ff9948">
<span>{{ item.date }} --- </span>{{ item.thing }}!!! Have a good day!
</p>
</template>
</todo-list>
todoList.vue则需要对slotName属性进行判断:若存在slotName属性,则渲染为通过访问 $scopedSlots
获取到自定义的插槽内容
<template>
<ul>
<li v-for="(item, index) in list" :key="index">
<!-- 无slotName -->
<template v-if="!item.slotName">
<span>{{ item.date }} :</span>
{{ item.thing }}
</template>
<!-- 存在slotName -->
<scopeItem v-else :option="item"></scopeItem>
</li>
</ul>
</template>
<script>
export default {
name: "todoList",
components: {
scopeItem: {
props: {
option: { required: true }
},
render(h) {
const {root: $parent, option} = this;
return root.$scopedSlots[option.slotName](option);
}
}
},
props: {
list: {
type: Array,
default() {
return [];
}
}
}
};
</script>
效果-->
为什么可以root.$scopedSlots[option.slotName](option)
(调用$scopedSlots.weekend
)?
console一下,输出可知其类型为 function
:
Last but not least
如有不妥,请多指教~