- 原文地址:https://github.com/vuejs/rfcs/blob/master/active-rfcs/0001-new-slot-syntax.md
- 作者:yyx990803, galchenkov,vaaski
- Git Commit:f38a8f2
以下为原文翻译
- 开始时间: 2019-01-14
- 目标主版本: 2.x & 3.x
- 相关Issues: https://github.com/vuejs/vue/issues/7740, https://github.com/vuejs/vue/issues/9180, https://github.com/vuejs/vue/issues/9306
- 实现 PR:
综述
引入一个新的作用域插槽语法:
新的指令
v-slot
使用同一个指令语法统一了slot
与slot-scope
的语法。v-slot
的简化写法可以同时统一有作用域的插槽和普通插槽。
基本示例
使用v-slot
来声明传入<foo>
作用域插槽的属性。
<!-- 默认插槽 -->
<foo v-slot="{ msg }">
{{ msg }}
</foo>
<!-- 命名插槽 -->
<foo>
<template v-slot:one="{ msg }">
{{ msg }}
</template>
</foo>
动机
在我们最初引入作用域插槽时,代码因为总是需要使用 <template slot-scope>
而变得十分啰嗦:
<foo>
<template slot-scope="{ msg }">
<div>{{ msg }}</div>
</template>
</foo>
为了简化代码,在2.5中我们赋予了插槽元素直接使用slot-scope
的能力:
<foo>
<div slot-scope="{ msg }">
{{ msg }}
</div>
</foo>
同时也可以直接用在插槽组件上:
<foo>
<bar slot-scope="{ msg }">
{{ msg }}
</bar>
</foo>
然而,上述的用法带来了一些问题:slot-scope
的位置并不能总是明确地反应作用域变量来自哪个组件。比如slot-scope
虽然声明在<bar>
组件上,但它实际上定义了一个由<foo>
的默认插槽提供的作用域变量。
这种情况随着嵌套加深愈来愈复杂:
<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 slot-scope="foo"> <!-- parent还是foo提供的? -->
{{ foo }}
</foo>
</parent>
这就是我认为在template之外使用slot-scope
是个错误的原因。
为什么使用新的指令而不是修改slot-scope
?
如果时间可以倒流,我可能会改变slot-scope
的语义,但:
现在这样做会导致breaking change,这意味着我们永远无法在2.x中使用这个特性。
即使我们在3.x中才改变已有语法的语义,在使用搜索引擎查询到过时的文档时,也会给未来的学习者带来非常多的问题。所以我们决定引入一个新的语法。
在3.x中,我们计划统一插槽类型使其不需要区分概念上的插槽和作用域插槽。一个插槽不论有没有属性都是插槽。随着概念的统一,就没有必要继续占用
slot
与slot-scope
两个特殊的属性了,并且在一个相同的结构下统一语法也是很好的选择。
细节设计
引入新指令: v-slot
.
-
他可以用在
<template>
插槽容器上代表插槽会传入组件,指令参数可以表示插槽的名字。<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>
如果一个插槽是作用域插槽有属性,我们可以使用指令值来声明插槽属性。
v-slot
的值与slot-scope
的值完全相同,所以支持JavaScript参数解构语法。<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>
-
作用域变量与提供其之组件之间的关系更清晰:
让我们再次观察上文中的深层嵌套例子,注意
<foo>
提供的作用域变量如何在<bar>
上声明等等。<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>
注意组件提供的作用域变量直接声明在组件自身上。新的语法使作用域变量与提供其之组件之间的关系更清晰。
更多用法比较
默认文本插槽
<!-- 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>
缺点
-
引入新语法会使大量的学习资料过时,新用户可能在学习已有的教程后接触新语法时感到困惑。
- 我们需要良好的文档说明语法的迁移。
默认的用法中
v-slot="{ msg }"
并未明确的表达出msg
应当作为参数传入插槽。
替代方案
- 新的特殊属性
slot-props
(之前版本的草案); - 用指令(
v-scope
)替代原先的特殊属性#9180; - 可替代的简写符号(
&
)由@rellect[提出];(https://github.com/vuejs/vue/issues/9306#issuecomment-453946190)
采用策略
这个特性完全向后兼容,所以我们可以在一个小版本中推出它(计划在2.6中)(实际上已发布)
slot-scope
会逐渐被废弃:首先它会在文档中标记为废弃,我们会鼓励所有人使用新语法,但是我们目前不会提示废弃消息,因为它在最新的语法迁移中并非最高优先级。
在3.0中我们确实希望移除slot-scope
用法,并只支持新语法。我们会发出废弃slot-scope
的信息以减轻迁移至3.0的压力。
由于这是一个良好定义的语法变化,我们可能提供一个自动化迁移工具来帮助你的模版文件进行迁移。
以上为原文翻译