概要
scoped slot 用法的新语法
slot
和slot-scope
合并成了v-slot
语法v-slot
可能 会统一slot
和slot-scope
基础示例
// 普通插槽
<foo v-slot="{ msg }">
{{ msg }}
</foo>
// 具名插槽
<foo>
<template v-slot:one="{ msg }">
{{ msg }}
</template>
</foo>
起因
我们一开始尝试scoped slots的时候,他非常冗长,因为要用<template slot-scope>
<foo>
<template slot-scope="{ msg }">
<div>{{ msg }}</div>
</template>
</foo>
为了让他简单点,在2.5版本,我们尝试将slot-scope
的能力直接使用在slot元素上
<foo>
<div slot-scope="{ msg }">
{{ msg }}
</div>
</foo>
也意味着可以用在组件上
<foo>
<bar slot-scope="{ msg }">
{{ msg }}
</bar>
</foo>
然而,上面这些用法导致了一个问题,slot-scope
的放置不能总是清晰地反应到底是哪个组件实际提供scope变量。
这里的slot-scope
作用在<bar />
组件上,但是它实际上是由<foo />
组件的提供的默认slot能力提供的。
在嵌套结构中,会出现问题
<foo>
<bar slot-scope="foo">
<baz slot-scope="bar">
<div slot-scope="baz">
{{ foo }} {{ bar }} {{ baz }}
</div>
</baz>
</bar>
</foo>
他不能随即清晰模板里哪个组件在提供变量
很多人建议我们应该在组件自身上使用slot-scope
,去表示他的默认插槽的作用域。
<foo slot-scope="foo">
{{ foo }}
</foo>
不幸的是,他在组件嵌套的时候模棱两可不能工作
<parent>
<!-- foo到底是parent还是foo的props -->
<foo slot-scope="foo">
{{ foo }}
</foo>
</parent>
这就是为什么我认为在template以外使用slot-scope
是个错误。
为什么要用新指令来代替修复slot-scope
如果我们可以时光倒流,我大概会改变slot-scope
的语义,但是
- 这现在是一个破坏式的改动,也就意味着我们不能用在 vue 2.x
- 就算在3.x改变了他,改变一个已经存在的语法,谷歌会成为过时的资料,导致很多未来学习者的混乱。我们想避免这件事,所以我们会采用新的不同于
slot-scope
的东西。 - 在3.x 我们想去统一
slot
的类型,所以没必要再去区分 scoped 和non-scoped slots(理论上的)。一个插槽可能接收props,也可能不接收,但是他们都是插槽。通过这种概念上的统一,slot
和slot-scope
两种不同属性的就没必要了,并且他在单一的结构下也会更好的去统一语法。
细节设计
介绍新指令:v-slot
- 可以在
<template>
的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>
如果任意作用域插槽是接收props的作用域插槽,接收的slot props可以声明在使用指令的属性里值里。v-slot
的值与 slot-scope
的工作方式相同,因此支持参数结构
<foo>
<template v-slot:header="{ msg }">
<div class="header">
Message from header slot: {{ msg }}
</div>
</template>
</foo>
v-slot
可以直接作用于组件不需要参数,来表明组件默认slot的scoped slot。并且props通过默认slot传递,也可以在属性里声明变量。
比较:New vs. Old
让我们回顾下改提案是否实现了我们概述的目标:
- 仍然为最通用的scoped slots用法(单默认插槽)提供了简洁的的语法
<foo v-slot="{ msg }">{{ msg }}</foo>
- 清除了scoped变量与提供他的组件的关联
让我们再看一下使用当前的语法(slot-scope)的深度嵌套,注意<foo>
提供而插槽范围变量是如何在<bar>
上声明的,bar上提供的变量是如何在<baz>
上声明的。
<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>
注意,由组件提供的作用域变量也在该组件本身上声明。新语法显示了插槽变量声明和提供变量的组件之间更清晰的联系。
更多用法比较
有text的默认插槽
<!-- old -->
<foo>
<template slot-scope="{ msg }">
{{ msg }}
</template>
</foo>
<!-- new -->
<foo v-slot="{ msg }">
{{ msg }}
</foo>
有元素的默认插槽
<!-- 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>
缺点
- 引入新语法会带来搅动,并且让有关这个话题的学习资料处在一个过时的生态里。(我们需要好的文档去帮助我们升级scoped slots)
- 默认插槽
v-slot="{ msg }"
不能准确地传递msg是作为prop被slot传递的。
选择
- 新的特殊属性
slot-props
- 指令
v-scope
代替最初提到的特殊属性 - 使用
&
代替v-slot
采纳策略
所做的更改完全向后兼容,所以我们可以在小版本中加入,计划是在2.6
slot-scope
已经被软弃用了:这会在文档里被标记,并且我们会鼓励每个人去使用/切换新语法,但是我们不会因为这个弃用消息来影响你,我们知道迁移新语法通常不是最高优的事情。
在3.0里,我们打算最终删除slot-scope,并且只支持新语法。我们会在下一个2.x的小版本发布废弃消息,从而简化迁移3.0。
既然这是一个定义明确的语法更改,我们可以提供一种迁移工具,该工具可以讲模板自动自动转化为新语法。