avatar

vue3的rfcs 更新东西 (中)

0014-drop-keycode-support(删除键码支持)

  • Start Date: 2019-11-08
  • Target Major Version: 3.x

总结

  • 放弃对使用数字(键码)作为 v-on 修饰符的支持
  • 移除 config.keyCodes

动机

在Vue2.x, v-on 已经支持使用任何有效的烤肉串版本KeyboardEvent.key作为修饰符,例如,当event.key === 'PageDown'仅仅去触发处理程序:

1
<input @keyup.page-down="onArrowUp">

这使得数字键码和config.keyCodes的冗余。此外[KeyboardEvent.keyCode已经被弃用]因此,Vue也应该停止支持它。

0015-remove-filters(移除过滤器)

  • Start Date: 2019-11-12
  • Target Major Version: 3.x

总结

移除对过滤器的支持

基础例子

1
2
3
4
5
<!-- before -->
{{ msg | format }}

<!-- after -->
{{ format(msg) }}

动机

  • 过滤器的功能可以通过方法调用或计算属性轻松复制,因此它主要提供的是语法价值,而不是实际价值。

  • 过滤器需要一个自定义的微语法来打破表达式“只是JavaScript”的假设——这增加了学习和实现的成本。事实上,它与JavaScript自己的位或运算符(‘ | ‘)相冲突,并使表达式解析更加复杂。

  • 过滤器还会在模板IDE支持中增加额外的复杂性(同样由于它们不是真正的JavaScript)

缺点

  • 当链接多个过滤器时,与调用多个函数相比,过滤器读起来更好:

    1
    2
    3
    msg | uppercase | reverse | pluralize
    // vs
    pluralize(reverse(uppercase(msg)))

    然而,在实践中,我们发现计数超过2的链接相当罕见,因此可读性损失似乎是可以接受的。

  • 单独导入或定义方法可能比全局注册的过滤器更容易一些。然而,全局过滤器与在Vue.prototype上注册特殊命名的全局helper没有本质区别。这种全局注册是有代价的:它们使代码依赖关系不那么显式,也使它们难以提供类型推断。

替代

目前有一个第一阶段的建议是将Pipeline操作符添加到JavaScript,它提供了很大程度上类似的语法便利:

1
let transformedMsg = msg |> uppercase |> reverse |> pluralize

考虑到这个提议有可能最终实现,像Vue这样的框架最好不要提供类似的替代方案(尤其是与现有JavaScript相冲突的语法)。

也就是说,该计划仍处于第一阶段,有一段时间没有收到更新,所以还不完全清楚它最终是否会降落,或者是否会按照现在的设计降落。Vue将其作为官方API的一部分采用是有风险的,因为如果规范最终发生更改,我们将被迫引入破坏性更改。

0016-remove-inline-templates(移除 inline-template)

  • Start Date: 2019-11-14
  • Target Major Version: 3.x

总结

移除对[inline-template 特色]的支持

动机

inline-template 最初包含在Vue中是为了解决使用Vue逐步增强传统服务器渲染的应用程序的情况(例如使用Rails、Django或Laravel)。它允许用户直接在父组件的模板中定义子组件的模板。

inline-template 最大的问题是它使模板的作用域非常不一致。如果没有 inline-template ,一个简单的经验法则是,出现在模板中的每个变量要么由所有者组件提供,要么由一个显式引入作用域变量的指令(e.g. v-for and v-slot)。inline-template通过在同一个模板中混合多个作用域上下文打破了这种假设:

1
2
3
4
5
6
<div>
{{ parentMsg }}
<child-comp inline-template>
{{ parentMsg }}
</child-comp>
</div>

在一个需要插槽的标准组件中, 可以直观地在插槽内容中工作。然而,对于 inline-template,情况就不再是这样了。类似地,带有v-for + inline-template 的组件也不会像预期的那样工作:

1
2
3
<child-comp inline-template v-for="item in list">
{{ item.msg }}
</child-comp>

在这里,内部模板实际上无法访问迭代的item。它指向 this.item。而不是子组件上的item。

0017-transition-as-root(transition 作为 根)

  • Start Date: 2019-11-29
  • Target Major Version: 3.x

总结

当组件从外部被切换时,使用<transition> 作为组件的根将不再触发转换。

基础例子

Before:

1
2
3
4
5
6
7
8
9
<!-- modal component -->
<template>
<transition>
<div class="modal"><slot/></div>
</transition>
</template>

<!-- usage -->
<modal v-if="showModal">hello</modal>

After: 暴露一个支柱来控制切换

1
2
3
4
5
6
7
8
9
<!-- modal component -->
<template>
<transition>
<div v-if="show" class="modal"><slot/></div>
</transition>
</template>

<!-- usage -->
<modal :show="showModal">hello</modal>

动机

目前的2.x的行为是偶然起作用的,但也有一些怪癖。我们并没有禁用该功能,而是添加了更多的修正以使其正常工作,因为有些用户依赖于这种行为。但是,这种用法在语义上是没有意义的:根据定义, <transition> 组件的工作方式是对其内部内容的切换做出反应,而不是对自身的切换:

1
2
3
4
5
6
7
8
9
<!-- this does not work -->
<transition v-if="show">
<div></div>
</transition>

<!-- this is expected usage -->
<transition>
<div v-if="show"></div>
</transition>

为了支持2.x行为,当涉及到确定过渡的appear 状态时,它也创建了许多复杂性。

详细设计

在3.0中,切换一个以<transition>作为根节点的组件将不再触发转换。相反,该组件应该暴露一个布尔属性来控制<transition>内部内容的存在。

缺点

在compat构建中,不能同时支持旧的行为和新行为。

改进策略

通过静态分析,可以发现对旧行为的依赖,检测根<transition> 组件的内部内容没有v-ifv-show指令。然后,迁移工具可以指导用户升级这些案例。

0018-transition-class-change (转换类变化)

总结

  • 重命名 the v-enter 转换类名为 v-enter-from
  • 重命名 the v-leave 转换类名为 v-leave-from
  • 重命名 the v-appear 转换类名为 v-appear-from

基础例子

1
2
3
4
5
6
7
8
9
/* before */
.v-enter, .v-leave-to{
opacity: 0;
}

/* after */
.v-enter-from, .v-leave-to{
opacity: 0;
}

动机

在v2.1.8之前,每个转换方向只有两个转换类。例如在“进入”转换中,我们有v-enterv-enter-active。在2.1.8版本中,我们引入了v-enter-to来解决[输入/离开转换之间的时间差距],但是,为了向后兼容,v-enter 名称没有被使用:

1
2
3
4
5
6
.v-enter, .v-leave-to {
opacity: 0;
}
.v-leave, .v-enter-to {
opacity: 1
}

不对称和缺乏显性.v-enter.v-leave 让这些类阅读和理解起来有点费劲。这就是为什么我们提议将上述内容改为:

1
2
3
4
5
6
.v-enter-from, .v-leave-to {
opacity: 0;
}
.v-leave-from, .v-enter-to {
opacity: 1
}

哪个更好地指示了这些类应用于什么状态。

详细设计

  • .v-enter 重命名为 .v-enter-from
  • .v-leave 重命名为 .v-leave-from
  • .v-appear 重命名为 .v-appear-from
  • The <transition> 组件的相关属性名称也会改变:
    • leave-class 重命名为 leave-from-class (in render functions or JSX, can be written as leaveFromClass)
    • enter-class 重命名为 enter-from-class (in render functions or JSX, can be written as enterFromClass)
    • appear-class 重命名为 appear-from-class (in render functions or JSX, can be written as appearFromClass)

策略

在compat构建中可以很容易地支持旧类名,并使用警告来指导迁移。

0019-remove-data-object-declaration(删除数据对象声明)

  • Start Date: 2020-01-10
  • Target Major Version: 3.x

总结

data选项接受两种类型的声明:functionobject 。最常见的是function声明,因为它会为组件的每个实例创建一个新的状态。另一方面, object声明在所有实例之间共享状态,并且只在根实例上工作。这个RFC主要关注于删除dataobject声明。

动机

根实例很少或没有共享状态的用例。即使遇到这种情况,也可以使用function声明来实现。
拥有两种类型的声明对新手来说并不友好,如果没有适当的示例(当前文档中不存在这些示例),就会令人混淆。另外,object声明只能在根实例上使用的限制也令人困惑。
通过统一的声明,您可以达到同样的结果,并消除其中令人困惑的部分。

详细设计

object 声明应该不再有效,并产生一个错误,解释说在根实例上只有 function 声明对data有效。它还应该包含到API和迁移示例的链接。

以前, 使用 object 声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { createApp, h } from 'vue'

createApp().mount({
data: {
counter: 1,
},
render() {
return [
h('span', this.counter),
h('button', {
onClick: () => { this.counter++ }
}),
]
},
}, '#app')

以后,使用 object 声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { createApp, h } from 'vue'

createApp().mount({
data() {
return {
counter: 1,
}
},
render() {
return [
h('span', this.counter),
h('button', {
onClick: () => { this.counter++ }
}),
]
},
}, '#app')

0020-events-api-change(事情api的改变)

  • Start Date: 2020-01-21
  • Target Major Version: 3.x

总结

删除$on, $off$once实例方法。Vue实例不再实现事件发射器接口。 前面三个 实际上是一种重载 常用$emit

0021-router-link-scoped-slot( router-link 和scoped-slot)

  • Start Date: 2019-04-29
  • Target Major Version: Vue (2.x / 3.x) Vue Router (3.x / 4.x)

总结

  • 移除tag 属性
  • 移除event 属性
  • 停止自动分配点击事件给内部锚点
  • 添加一个作用域插槽API
  • 添加一个custom 属性来完全自定义router-link的渲染

基础例子

1
2
3
<router-link to="/">
<Icon>home</Icon> Home
</router-link>

动机

当前路由器链路的实现有很多限制:

  • 激活状态定制未完成
  • 不能与自定义组件集成
  • 点击事件不能被阻止(通过@click.prevent而不是通过disabled 属性
    这个RFC的想法是通过提供一个限定范围的插槽来解决这些问题,该插槽允许应用程序开发人员轻松地扩展基于其应用程序的链接,并允许库作者更容易地提供与Vue Router的集成。

0022-router-merge-meta-routelocation(路由器合并元路由位置)

  • Start Date: 2020-03-12
  • Target Major Version: Router 4.x

总结

当创建路由时,你可以用meta属性附加任意的数据:

1
{ path: '/profile', meta: { requiresAuth: true }}

然后可以在导航守卫和$route中访问它:

1
2
3
4
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !auth.loggedIn()) next('/login')
else next()
})

然而,当处理嵌套路由时,meta将只包含匹配的路由meta。你仍然可以像[在文档中指出]那样遍历matched记录数组:

1
2
3
4
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth))
// ...
})

我的建议是合并所有匹配的路由元,从父到子,所以我们可以做 to.meta.requiresAuth。我相信这就是Nuxt所做的,但我在文档中找不到一个链接。

基础例子

给定一个嵌套的路由:

1
2
3
4
5
6
7
{
path: '/parent',
meta: { requiresAuth: true, isChild: false },
children: [
{ path: 'child', meta: { isChild: true }}
]
}

导航到/parent/child应该会生成一个带有meta属性的路由:

1
{ requiresAuth: true, isChild: true }

0023-scoped-styles-changes

  • Start Date: 2020-01-21
  • Target Major Version: 2.x, 3.x

总结

在单个文件组件范围的样式中提供更一致的自定义CSS扩展。

基础例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style scoped>
/* deep selectors */
::v-deep(.foo) {}
/* shorthand */
:deep(.foo) {}

/* targeting slot content */
::v-slotted(.foo) {}
/* shorthand */
:slotted(.foo) {}

/* one-off global rule */
::v-global(.foo) {}
/* shorthand */
:global(.foo) {}
</style>

0024-attribute-coercion-behavior(属性强制行为)

  • Start Date: 2020-01-30
  • Target Major Version: 3.x

总结

  • 删除枚举属性的内部概念,并将这些属性视为普通的非布尔属性。
  • 不再移除值为boolean false的属性。相反,它被设置为attr="false"。要删除该属性,请使用nullundefined

0025-teleport

  • Start Date: 2020-01-20
  • Target Major Version: (3.x)

总结

  • 在Vue核心添加了一个<teleport>组件
  • 组件需要一个目标元素,通过一个期待HTMLElementquerySelector 字符串的属性提供。
  • 组件将它的子元素移动到DOM选择器所标识的元素上
  • 在虚拟DOM级别,孩子们仍然是<teleport>的后代,所以他们可以从祖先那里获得注射

基础例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<div id="app">
<h1>Move the #content with the portal component</h1>
<teleport to="#endofbody">
<div id="content">
<p>
this will be moved to #endofbody.<br />
Pretend that it's a modal
</p>
<Child />
</div>
</teleport>
</div>
<div id="endofbody"></div>
<script>
new Vue({
el: "#app",
components: {
Child: { template: "<div>Placeholder</div>" }
}
});
</script>
</body>

这将导致以下行为:

  1. <teleport>的所有子元素——在这个例子中:<div id="content"><Child /> ——将被添加到<div id="endofbody">中。
  2. 作为这些子组件之一的<Child>组件仍然是<teleport>的父组件的子组件(<teleport>是透明的)。
1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<!-- -->
</div>
<div id="endofbody">
<div id="content">
<p>
this will be moved to #endofbody.<br />
Pretend that it's a modal
</p>
<div>Placeholder</div>
</div>
</div>

动机

Vue鼓励我们通过将UI和相关行为封装到组件中来构建UI,我们可以将组件彼此嵌套在一起,构建组成应用程序UI的组件树。在Vue和其他框架中,这个模型已经在很多方面证明了自己,但是RFC试图解决一个缺点:
有时,一个组件的模板的一部分属于这个组件logically,虽然从技术的角度(例如:样式需求),这将是比别的地方转的这一部分模板在DOM中,打破它的深层嵌套的位置没有我们的DOM树。

使用例子

在模板中,编译器会把 <teleport>组件的导入添加到生成的代码中,所以它可以像这样使用:

1
2
3
4
5
6
7
8
9
export default {
template: `
<div>
<teleport to="#endofbody">
Some content.
</teleport>
<div>
`
};

当使用渲染函数或JSX时,组件必须先导入,就像其他组件一样:

1
2
3
4
5
6
7
8
9
10
11
12
import { Teleport, h } from "vue";
export default {
render() {
return h("div", [h(Teleport, { to: "#endofbody" }, ["Some content"])]);
},
// or with JSX:
render() {
<div>
<Teleport to="#endofbody">Some content</Teleport>
</div>;
}
};

案例

1
2
3
4
5
6
7
8
9
10
11
12
<teleport to="#modals">
<div>A</div>
</teleport>
<teleport to="#modals">
<div>B</div>
</teleport>

<!-- result-->
<div id="modals">
<div>A</div>
<div>B</div>
</div>

to

1
2
3
4
5
6
7
8
9
10
11
12
<!-- ok -->
<teleport to="#some-id" />
<teleport to=".some-class" />
<teleport to="[data-portal]" />
<!--
probably too unspecific, but technically valid
should we allow this or block it?
-->
<teleport to="h1" />
<!-- Wrong -->
<teleport to="some-string" />
></teleport>

disabled

1
2
3
<teleport to="#popup" :disabled="displayVideoInline">
<video src="./my-movie.mp4">
</teleport>

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<teleport v-bind:to="targetName">
<p>This can be moved around with the button below</p>
</teleport>
<button v-on:click="toggleTarget">Toggle</button>
<hr />
<div id="A"></div>
<div id="B"></div>
</template>
<script>
export default {
data: () => ({
targetName: "A"
}),
methods: {
toggleTarget() {
this.targetName = this.targetName == "A" ? "B" : "A";
}
}
};
</script>
文章作者: 张复明
文章链接: https://hexo.zhangaming.com/2021/01/26/vue3-active-rfcs-mid/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿明的博客
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论