Vue3(其实从2.6开始)中引入了一个新的指令v-slot ,用来表示具名插槽和默认插槽
基础示例
<!-- default slot -->
<foo v-slot="{ msg }">
{{ msg }}
</foo>
<!-- named slot -->
<foo>
<template v-slot:one="{msg}">
{{ msg }}
</template>
</foo>
为什么要这么做
<foo>
<bar slot-scope="foo">
<baz slot-scope="bar">
<div slot-scope="baz">
{{ foo }} {{ bar }} {{ baz }}
</div>
</baz>
</bar>
</foo>
?像上面这种情况,我们无法立即分辨出模板上作用域变量是由那一个组件提供的。
引入一个新的指令v-slot
我们可以在slot容器<template> 上使用v-slot 来表示一个传入组件的插槽,通过指令参数来表示插槽的名称。
<foo>
<template v-slot:header>
<div class="header"></div>
</template>
<template v-slot:body>
<div class="body"></div>
</template>
<template v-slot:footer>
<div class="footer"></div>
</template>
</foo>
作用域插槽的内部工作原理是将插槽的内容包裹在一个函数里
function(slotProps){
// 插槽内容
}
这就意味着v-slot 的值实际上可以是任何能够作为函数定义中的参数的Javascript表达式,所以在支持的环境下 (单文件组件或现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:
<foo>
<template v-slot:header="{ msg }">
<div class="header">
Message from header slot: {{ msg }}
</div>
</template>
</foo>
v-slot 可以直接用在组件上,如果没有参数,则表示默认的作用域插槽,传递给默认插槽的属性应该作为变量在其属性值声明
<foo v-slot="{ msg }">
{{ msg }}
</foo>
新旧语法对比
对于常用的场景(只有一个默认的作用域插槽)
<foo v-slot="{msg}">{{msg}}</foo>
作用域变量与组件之间的联系更加的清晰
<!-- old -->
<foo>
<bar slot-scope="foo">
<baz slot-scope="bar">
<div slot-scope="baz">
{{ foo }} {{ bar }} {{ baz }}
</div>
</baz>
</bar>
</foo>
使用新语法也可以达到同样的效果
<foo v-slot="foo">
<bar v-slot="bar">
<baz v-slot="baz">
{{ foo }} {{ bar }} {{ baz }}
</baz>
</bar>
</foo>
动态插槽名
动态指令参数也可以用在?v-slot ?上,来定义动态的插槽名:
<foo>
<template v-slot:[slotName]>
...
</template>
</foo>
插槽指令的缩写
和?v-bind 和v-on 相似,缩写只有在存在参数时才生效,这就意味着v-slot 没有参数时不能使用#= ,对于默认插槽,可以使用#default 来代替v-slot
<!-- full syntax -->
<foo>
<template v-slot:header="{ msg }">
Message from header: {{ msg }}
</template>
<template v-slot:footer>
A static footer
</template>
</foo>
<!-- shorthand -->
<foo>
<template #header="{ msg }">
Message from header: {{ msg }}
</template>
<template #footer>
A static footer
</template>
</foo>
<foo v-slot="{ msg }">
{{ msg }}
</foo>
<foo #default="{ msg }">
{{ msg }}
</foo>
更多的用例对比
默认的文本作用域插槽
<!-- old -->
<foo>
<template slot-scope="{ msg }">
{{ msg }}
</template>
</foo>
<!-- new -->
<foo v-slot="{ msg }">
{{ msg }}
</foo>
默认的含DOM节点的插槽
<!-- old -->
<foo>
<div slot-scope="{msg}">
{{msg}}
</div>
</foo>
<!-- new -->
<foo v-slot="{msg}">
<div>
{{msg}}
</div>
</foo>
嵌套的默认插槽
<!-- old -->
<foo>
<bar slot-scope="foo">
<baz slot-scope="bar">
<template slot-scope="baz">
{{ foo }} {{ bar }} {{ baz }}
</template>
</baz>
</bar>
</foo>
<!-- new -->
<foo v-slot="foo">
<bar v-slot="bar">
<baz v-slot="baz">
{{ foo }} {{ bar }} {{ baz }}
</baz>
</bar>
</foo>
命名插槽?
<!-- old -->
<foo>
<template slot="one" slot-scope="{ msg }">
text slot: {{ msg }}
</template>
<div slot="two" slot-scope="{ msg }">
element slot: {{ msg }}
</div>
</foo>
<!-- new -->
<foo>
<template v-slot:one="{ msg }">
text slot:{{msg}}
</template>
<template v-slot:two="{msg}">
<div>
element slot: {{msg}}
</div>
</template>
</foo>
嵌套 & 命名 / 默认
<!-- old -->
<foo>
<bar slot="one" slot-scope="one">
<div slot-scope="bar">
{{ one }} {{ bar }}
</div>
</bar>
<bar slot="two" slot-scope="two">
<div slot-scope="bar">
{{ two }} {{ bar }}
</div>
</bar>
</foo>
<!-- new -->
<foo>
<template v-slot:one="one">
<bar v-slot="bar">
<div>{{ one }} {{ bar }}</div>
</bar>
</template>
<template v-slot:two="two">
<bar v-slot="bar">
<div>{{ two }} {{ bar }}</div>
</bar>
</template>
</foo>
具名插槽
当有多个插槽时,插槽增加了name 属性来正确渲染对应的部分,父组件需要使用<template></template> 。可以认为匿名插槽是特殊的具名插槽。
<!-- my-layout子组件定义 -->
<template>
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<!-- 其实就是<slot name="default"></slot> -->
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父组件调用my-layout -->
<my-layout>
<!-- 填充内容顺序无关 -->
<template v-slot:footer>
<p>我来组成脚丫子</p>
</template>
<!-- v-slot:header可以简写为#header -->
<template v-slot:header>
<h1>我来组成头部</h1>
</template>
<!-- v-slot:default可以忽略不写 -->
<template v-slot:default>
<p>我来组成身体</p>
</template>
</my-layout>
<!-- 父组件页面渲染为 -->
<div>
<header>
<h1>我来组成头部</h1>
</header>
<main>
<p>我来组成身体</p>
</main>
<footer>
<p>我来组成脚丫子</p>
</footer>
</div>
|