<slot name="header" >
对于这样的情况,`` 元素有一个特殊的 attribute:name
。通过它可以为不同的插槽分配独立的 ID,也就能够以此来决定内容应该渲染到什么地方:
<div class="container">
<header>
<slot name="header">slot>
header>
<main>
<slot>slot>
main>
<footer>
<slot name="footer">slot>
footer>
div>
name
的 slot 出口会带有隐含的名字“default”。在向具名插槽提供内容的时候,我们可以在一个 元素上使用
v-slot指令,并以
v-slot` 的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page titleh1>
template>
<template v-slot:default>
<p>A paragraph for the main content.p>
<p>And another one.p>
template>
<template v-slot:footer>
<p>Here's some contact infop>
template>
base-layout>
缩写形式:
2.6.0 新增(也是在2.6.0开始废弃了 slot 和 slot-scope的语法的·)
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
<base-layout>
<template #header>
<h1>Here might be a page titleh1>
template>
<template #default>
<p>A paragraph for the main content.p>
<p>And another one.p>
template>
<template #footer>
<p>Here's some contact infop>
template>
base-layout>
我的理解作用域插槽是为了让子组件的数据被父组件拿到。也可以理解为让子组件给父组件传递数据。子组件给父组件传递数据用emit不就行了吗。但emit有个缺陷。就是多级(三级以及以上)组件有底层组件向上传递时候。难道要一层一层emit吗?
还有就是让中间层组件不处理业务逻辑。顶层组件处理业务逻辑,中间层组件只生成样式。
定义孙子组件:
List.vue
<template>
<div>
<h3>商品列表h3>
<div>
{{ ListData }}
div>
div>
template>
<script>
import { reactive, toRefs, ref } from "vue";
export default {
props: {
ListData: Object,
},
setup() {
const clickModify = (item) => {
console.log(item);
};
return { clickModify };
},
};
script>
父组件:
AboutChild.vue
<template>
<div>
<div class="for">
<h3 v-for="(item, index) in column" :key="index">
<slot :item="item">slot>
h3>
div>
div>
template>
<script>
import { reactive, toRefs, refs } from "vue";
export default {
props: {
column: Object,
},
setup() {
const hobbies = [
{ name: "a", age: 12 },
{ name: "b", age: "123" },
{ name: "ccc", age: 123 },
{},
];
const data = reactive({ hobbies });
return { ...toRefs(data) };
},
};
script>
爷爷组件:
AbouView.vue
<template>
<div>
<AboutChild
v-for="item in columns"
:key="item.name"
:column="item.childrenList"
>
<template #default="AAA">
<List :ListData="AAA.item" />
template>
AboutChild>
div>
template>
<script>
import { reactive, toRefs, ref } from "vue";
import AboutChild from "./children/AboutChild";
import List from "./children/children/List";
export default {
components: { AboutChild, List },
setup() {
let name = ref("name");
let data = reactive({
columns: [
{
name: "好购物",
childrenList: [
{
title: "a",
},
{
title: "b",
},
{
title: "c",
},
{
title: "e",
},
],
},
{
name: "新青年",
childrenList: [
{
title: "A",
},
{
title: "B",
},
{
title: "C",
},
{
title: "D",
},
],
},
],
});
return {
name,
...toRefs(data),
};
},
};
script>
把爷爷组件里的内容拎出来:
<AboutChild
v-for="item in columns"
:key="item.name"
:column="item.childrenList"
>
<template v-slot:default="AAA">
<List :ListData="AAA.item" />
template>
AboutChild>
v-slot:default="AAA" -----> #default="AAA"(element-plus中用了大量的这种缩写)
为啥v-slot后面跟的是 default呢? ====> 因为slot如果没有name属性的话, v-slot:default 匹配这些没有命名的slot; 有名字的话要 v-slot:名字 ="AAA" 来匹配
也就是说我如果 是 <slot name="yazhou" :item="item"> </slot> 那我就要写 v-slot:yazhou="AAA"
在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上:
<todo-list v-slot:default="slotProps">
<i class="fas fa-check">i>
<span class="green">{{ slotProps.item }}span>
todo-list>
这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot
被假定对应默认插槽:
<todo-list v-slot="slotProps">
<i class="fas fa-check">i>
<span class="green">{{ slotProps.item }}span>
todo-list>
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
!-- 无效,会导致警告 -->
<todo-list v-slot="slotProps">
<i class="fas fa-check">i>
<span class="green">{{ slotProps.item }}span>
<template v-slot:other="otherSlotProps">
slotProps 在此处不可用
template>
todo-list>
只要出现多个插槽,请始终为所有的插槽使用完整的基于 ` 的语法:
<todo-list>
<template v-slot:default="slotProps">
<i class="fas fa-check">i>
<span class="green">{{ slotProps.item }}span>
template>
<template v-slot:other="otherSlotProps">
...
template>
todo-list>
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:
function (slotProps) {
// 插槽内容
}
这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件或现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:
<current-user v-slot="{ user }">
{{ user.firstName }}
current-user>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 user
重命名为 person
:
<current-user v-slot="{ user: person }">
{{ person.firstName }}
current-user>
v-slot 指令自 Vue 2.6.0 起被引入,提供更好的支持 slot 和 slot-scope attribute 的 API 替代方案。v-slot 完整的由来参见这份 RFC。在接下来所有的 2.x 版本中 slot 和 slot-scope attribute 仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。
在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot
指令)。它取代了 slot
和 slot-scope
这两个目前已被废弃但未被移除且仍在文档中的 attribute。新语法的由来可查阅这份 RFC。
<slot name="header" />
在
上使用特殊的 slot
attribute,可以将内容从父级传给具名插槽 (把这里提到过的 `` 组件作为示例):
<base-layout>
<template slot="header">
<h1>Here might be a page titleh1>
template>
<p>A paragraph for the main content.p>
<p>And another one.p>
<template slot="footer">
<p>Here's some contact infop>
template>
base-layout>
或者直接把 slot
attribute 用在一个普通元素上:
<base-layout>
<h1 slot="header">Here might be a page titleh1>
<p>A paragraph for the main content.p>
<p>And another one.p>
<p slot="footer">Here's some contact infop>
base-layout>
这里其实还有一个未命名插槽,也就是默认插槽,捕获所有未被匹配的内容。
上述两个示例的 HTML 渲染结果均为:
<div class="container">
<header>
<h1>Here might be a page titleh1>
header>
<main>
<p>A paragraph for the main content.p>
<p>And another one.p>
main>
<footer>
<p>Here's some contact infop>
footer>
div>
带有
slot-scope
attribute 的作用域插槽 ( 自 2.6.0 起被废弃。新推荐的语法请查阅这里。 )
在
上使用特殊的 slot-scope
attribute,可以接收传递给插槽的 prop (把这里提到过的 `` 组件作为示例):
<slot-example>
<template slot="default" slot-scope="slotProps">
{{ slotProps.msg }}
template>
slot-example>
这里的 slot-scope
声明了被接收的 prop 对象会作为 slotProps
变量存在于 作用域中。你可以像命名 JavaScript 函数参数一样随意命名
slotProps`。
这里的 slot="default"
可以被忽略为隐性写法:
<slot-example>
<template slot-scope="slotProps">
{{ slotProps.msg }}
template>
slot-example>
slot-scope
attribute 也可以直接用于非 ` 元素 (包括组件):
<slot-example>
<span slot-scope="slotProps">
{{ slotProps.msg }}
span>
slot-example>
slot-scope
的值可以接收任何有效的可以出现在函数定义的参数位置上的 JavaScript 表达式。这意味着在支持的环境下 (单文件组件或现代浏览器),你也可以在表达式中使用 ES2015 解构,如下:
<slot-example>
<span slot-scope="{ msg }">
{{ msg }}
span>
slot-example>
使用这里描述过的
作为示例,与它等价的使用 slot-scope
的代码是:
<todo-list v-bind:todos="todos">
<template slot="todo" slot-scope="{ todo }">
<span v-if="todo.isComplete">✓span>
{{ todo.text }}
template>
todo-list>