- 开始时间:2019-03-12
- 目标主要版本:3.x
- 引用 issue:N/A
- 实现的 PR:N/A
摘要
统一普通插槽和作用域插槽的概念。在 v3 中都只是插槽。
基本范例
N/A
动机
- 普通插槽和作用域插槽的分离是由于作用域插槽后来作为一个新的概念被添加,它们在 2.x 中有不同的内部实现。统一这两者可以简化插槽整体的概念。
- 使用渲染函数的作者们不用再担心同时处理
$slots和$scopedSlots了。 - 将所有插槽编译成函数,在大型组件树中会有更好的性能。
引用 2.6 发布的公告:
正常插槽是在父元素的渲染周期中进行渲染的。当一个插槽的依赖关系发生改变时,会导致父组件和子组件重新渲染。另一方面,作用域插槽被编译成内联函数,并在子组件的渲染周期中被调用。这意味着子组件会收集作用域插槽所依赖的任何数据,从而获得更精准的更新。在 2.6 中,我们引入了一项优化,进一步确保父级范围的依赖性突变只能影响到父级,如果子组件只使用作用域插槽,将不再强制更新。
具体设计
插槽的统一涉及两个部分:
- 语法的统一(在 2.6 中作为 v-slot 提供)
- 实现统一:将所有插槽编译成函数。
this.$slots现在将插槽作为函数公开。this.$scopedSlots将被移除。- 在 2.x 中,所有使用
v-slot语法的插槽在内部已经被编译成函数。this.$scopedSlot也代理为普通插槽,并且将它们暴露为函数。
在渲染函数中的用法
现有的渲染函数用法将继续被支持。当将子元素传递给组件时,VNodes 和函数都被支持:
h(Comp, [h('div', this.msg)])// equivalent:h(Comp, () => [h('div', this.msg)])
在 Comp 内部,this.$slots.default 在两种情况下都是一个函数,并且返回一个 VNodes。然而,第二个例子会更具表现力,因为 this.msg 将只会被注册为子组件的依赖关系。
具名插槽的用法已经改变 —— 不在内容节点上设置特殊的插槽数据属性,而是通过第三个参数将它们作为 children 传递:
// 2.xh(Comp, [h('div', { slot: 'foo' }, this.foo),h('div', { slot: 'bar' }, this.bar)])// 3.0// Note the `null` is required to avoid the slots object being mistaken// for props.h(Comp, null, {foo: () => h('div', this.foo),bar: () => h('div', this.bar)})
更进一步的手动优化
注意当父组件更新,Comp总是要被迫更新,因为如果没有编译步骤,Vue 就没有足够的信息来判断插槽是否发生了变化。
编译器可以检测到 v-slot 并将内容编译成函数,但在渲染函数中,这不会自动发生。我们也可能在我们的 JSX babel 插件中进行类似的优化。但是对于直接写渲染函数的用户来说,它们需要在性能敏感的用例中手动处理。
插槽可以手动被注解,这样 Vue 就不会在父组件更新时强制更新子组件:
h(Comp, null, {$stable: true,foo: () => h('div', this.foo),bar: () => h('div', this.bar)})
缺点
N/A
备选方案
N/A
采纳策略
大部分的变化已经在 2.6 中已经发布了。唯一剩下的就是将 this.$scopedSlots 从 API 中移除。实际上,目前在 2.6 中 this.$scopedSlots 与 3.0 中的 this.$slots 工作原理完全相同,所以迁移可以分为两步:
- 在 2.x 中到处都可以使用 this.$scopedSlots;
- 在 3.x 中要将 this.$scopedSlots 替换成 this.$slots。
没有解决的问题
N/A
