【翻译】Vue RFCs 0001-new-slot-syntax

以下为原文翻译


综述

引入一个新的作用域插槽语法:

  • 新的指令v-slot使用同一个指令语法统一了slotslot-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的语义,但:

  1. 现在这样做会导致breaking change,这意味着我们永远无法在2.x中使用这个特性。

  2. 即使我们在3.x中才改变已有语法的语义,在使用搜索引擎查询到过时的文档时,也会给未来的学习者带来非常多的问题。所以我们决定引入一个新的语法。

  3. 在3.x中,我们计划统一插槽类型使其不需要区分概念上的插槽和作用域插槽。一个插槽不论有没有属性都是插槽。随着概念的统一,就没有必要继续占用slotslot-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应当作为参数传入插槽。

替代方案

采用策略

这个特性完全向后兼容,所以我们可以在一个小版本中推出它(计划在2.6中)(实际上已发布)

slot-scope会逐渐被废弃:首先它会在文档中标记为废弃,我们会鼓励所有人使用新语法,但是我们目前不会提示废弃消息,因为它在最新的语法迁移中并非最高优先级。

在3.0中我们确实希望移除slot-scope用法,并只支持新语法。我们会发出废弃slot-scope的信息以减轻迁移至3.0的压力。

由于这是一个良好定义的语法变化,我们可能提供一个自动化迁移工具来帮助你的模版文件进行迁移。


以上为原文翻译

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容