- 开始时间:2019-01-14
- 目标主要版本:2.x 和 3.x
- 引用 issue:https://github.com/vuejs/vue/issues/7740,https://github.com/vuejs/vue/issues/9180,https://github.com/vuejs/vue/issues/9306
- 实现的 PR:(leave this empty)
摘要
为 scoped slot 的使用引入了一种新语法:
- 新的 v-slot 指令将 slot 和 slot-scope 统一到一个指令语法中。
- v-slot 的简写有可能统一普通插槽和作用域插槽的使用。
基本范例
用 v-slot 来声明传递给
<!-- default slot --><foo v-slot="{ msg }">{{ msg }}</foo><!-- named slot --><foo><template v-slot:one="{ msg }">{{ msg }}</template></foo>
动机
当我们第一次引入作用域插槽时,由于需要一直使用 <template slot-scope>,所以很啰嗦(verbose):
<foo><template slot-scope="{ msg }"><div>{{ msg }}</div></template></foo>
为了使它不那么冗长,在 2.5 中我们直接引入了在 slot 元素上使用 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"> <!-- provided by parent or by foo? -->{{ foo }}</foo></parent>
这就是为什么我现在认为允许用没有模版的 slot-scope 是一个错误。
为什么是一个新的指令,而不是修复 slot-scope?
如果我们能回到过去,我可能会改变 slot-scope 的语义,但是:
- 这将是一个突破性的变化,这意味着我们将永远无法在 2.x 中发布它。
- 即使我们在 3.x 中改变了它,改变现有语法的语义也会给未来的学习者带来很多困惑,因为谷歌的学习资料已经过时。我们绝对(definitely)希望避免这种情况。所以,我们必须引入一些新东西来区别 slot-scope
- 在 3.x 中,我们计划统一 slot 类型,这样就不需要再区分作用域和非作用域插槽(概念上)。一个插槽可以接受也可以不接受 props,但他们都只是插槽。有了这种概念上的统一,
slot和slot-scope这两个特殊的属性似乎没有必要了,而且把语法统一在一个结构下也会很好。
具体设计
引入一个新指令: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>如果任何一个插槽是一个接收
props的作用域插槽,那么接收props的插槽可以使用指令的属性值来声明。v-slot的值与slot-scope的工作方式相同,所以支持 JavaScript 的参数结构。<foo> <template v-slot:header="{ msg }"> <div class="header"> Message from header slot: {{ msg }} </div> </template> </foo>v-slot 可以直接用在一个组件上,没有参数,表示该组件的默认插槽是一个作用域插槽,传递给默认插槽的 props 应该可以作为其属性值中声明的变量。
<foo v-slot="{ msg }"> {{ msg }} </foo>新的与旧的比较
让我们回顾一下这个提案是否实现了我们上述的目标:
对于作用域插槽(单个默认插槽)的最常见使用情况,仍提供简洁的语法:
<foo v-slot="{ msg }">{{ msg }}</foo>作用域内的变量和提供它的组件之间的联系更加清晰:
让我们再看看使用当前语法(slot-scope)的深层嵌套例子 —— 注意提供的作用域变量是如何在 上声明的,而 提供的变量是在 上声明的…… <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 作为了 props 被传递到插槽中的概念。
备选方案
- 新的特殊属性 slot-scope(作为本草案的前一个版本)。
- 指令(v-scope),而不是 #9180 中最初提议的特殊属性。
- 如同 @rellect 在这建议的那样,使用替代的速记符号(&)。
采纳策略
这个变化是完全向后兼容的,所以我们可以在一个小版本中推出它(计划在 2.6 版本中)。
slot-scope将被弃用:它将会在文档中标记为弃用,我们鼓励每一个人使用/切换到新的语法,但是我们不会用弃用信息来烦扰你,因为我们知道对每个人来说,总是迁移到新的东西不是最重要的。在 3.0 中,我们确实计划最终删除
slot-scope,只支持新的语法。我们将在下一个 2.x 的版本中开始发布关于slot-scope使用的弃用信息,以简化向 3.0 的迁移。由于这是一个非常明确的语法变化,我们有可能会提供一个迁移工具,可以自动将你的模版转化为新的语法。
