Vue3 RFCS导读
# # Vue3 RFCS导读
为了对即将到来的vue3有个全面的认识,通读Vue3 rfcs (opens new window) (opens new window)(意见修改稿)是有必要的。但原版英文内容比较长,通读时间比较耗时。这里笔者根据原文内容总结输出,方便大家对Vue3细节改动有个全局的认识。更多详细设计请在每个章节链接进去查看。
# # 1. componsition api (opens new window) (opens new window)
核心变动,增加setup选项
,更多动机和设计看官方教程 (opens new window) (opens new window)
// 基础示例
<template>
<button @click="increment">
Count is: {{ state.count }}, double is: {{ state.double }}
</button>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
setup() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
})
function increment() {
state.count++
}
return {
state,
increment
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# # 2. slot统一
- 模板上slot和slot-scope统一为
v-slot
- 模板上支持
v-slot
简写为#
- this.$slots和this.$scopedSlots
统一为this.$slots
,并且暴露为funciton函数。(ps:集合了this.$slots和this.$scopedSlots各自特点)
相关rfcs:
- https://github.com/vuejs/rfcs/blob/master/active-rfcs/0001-new-slot-syntax.md
- https://github.com/vuejs/rfcs/blob/master/active-rfcs/0002-slot-syntax-shorthand.md
- https://github.com/vuejs/rfcs/blob/master/active-rfcs/0006-slots-unification.md
# # 3. 动态指令参数 (opens new window) (opens new window)
指令参数支持动态设置
<div v-bind:[key]="value"></div>
<div v-on:[event]="handler"></div>
2
3
# # 4. v-model变动
- v-model支持参数: 使用
v-model:arg
语法 代替:arg.sync
- 不带参数的v-model,默认事件名为
update:modelValue
,而不是以前的事件名:input。(ps:主要还是统一上一条变动)
相关rfcs:
- https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md
- https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md
<!--模版里不再有.sync语法-->
<MyComponent v-model:title="title" />
2
3
# # 5. functional api去除 (opens new window) (opens new window)
关键点:
- 不再需要有functional选项api
函数式组件不再是对象,而变成类React函数式
- 入参有变化
// 基本展示
import { h } from 'vue'
const FunctionalComp = (props, { slots, attrs, emit }) => {
return h('div', `Hello! ${props.name}`)
}
2
3
4
5
6
7
# # 6. 全局api tree shaking (opens new window) (opens new window)
所有挂载在Vue对象的方法,都单独出去了,比如Vue.nextTick、Vue.observable
# # 7. render api修改 (opens new window) (opens new window)
关键点:
h函数全局导入
h函数参数统一
,不管是有状态的组件还是函数式组件VNodes数据结构优化展平
(ps:非常实用的改动,写jsx简单了)
// 全局导入 `h`函数
import { h } from 'vue'
export default {
render() {
return h(
'div',
// // vnode数据结构更直观
{
id: 'app',
onClick() {
console.log('hello')
}
},
[
h('span', 'child')
]
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# # 8. 增加createApp api (opens new window) (opens new window)
创建app实例,而不是像以前共享同一个Vue实例
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.config.isCustomElement = tag => tag.startsWith('app-')
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
app.config.globalProperties.customProperty = () => {}
app.mount(App, '#app')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# # 9. 自定义指令api变更 (opens new window) (opens new window)
自定义指令跟vue hook命名一致
const MyDirective = {
beforeMount(el, binding, vnode, prevVnode) {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {}, // new
unmounted() {}
}
2
3
4
5
6
7
8
9
# # 10. 废弃keycode修饰符 (opens new window) (opens new window)
因为KeyboardEvent.keyCode (opens new window) (opens new window)已经被废弃,故在vue3中移除
# # 11. 移除filter api (opens new window) (opens new window)
使用方法或computed
代替filter
<!-- before -->
{{ msg | format }}
<!-- after -->
{{ format(msg) }}
2
3
4
5
6
# # 12. 组件过渡类名重命名 (opens new window) (opens new window)
from/to名称对称,更好理解
- v-enter 重命名为
v-enter-from
- v-leave 重命名为
v-leave-from
.v-enter-from, .v-leave-to {
opacity: 0;
}
.v-enter-active {
opacity: 0.5;
}
.v-leave-from, .v-enter-to {
opacity: 1
}
.v-leave-active {
opacity: 0.5;
}
2
3
4
5
6
7
8
9
10
11
12
13
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter-from 会替换为 my-transition-enter-from
# # 13. 响应式data api只支持function方式 (opens new window) (opens new window)
以前2.x响应式data定义,支持object和function方式(当然大部分人约定俗成的都使用function方式),在vue3中强制data只能用function。
因为如果使用object定义data时,当有多个组件实例共用同一个引用类型data,容易造成错乱。
import { createApp, h } from 'vue'
createApp().mount({
data() { // data一定是个函数
return {
counter: 1,
}
},
render() {
return [
h('span', this.counter),
h('button', {
onClick: () => { this.counter++ }
}),
]
},
}, '#app')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# # 14. 移除$on, $off, $once (opens new window) (opens new window)
Vue3不再提供事件发布接口,如果有发布事件需要,可以使用mitt (opens new window) (opens new window)库代替
# # 15. 嵌套路由meta自动合并 (opens new window) (opens new window)
以前处理页面权限时,常使用meta作为配置方案,当匹配到单个页面时,判断to.meta.requiresAuth即可。但在嵌套路由页面时,子路由页面一般没有再设置requiresAuth,所以在Vue2.x中只能通过to.matched
获得匹配数组再逻辑判断。
Vue3提供了嵌套路由meta的自动合并,使得逻辑判断更加简单。注意这里的合并是Object.assign浅拷贝。
加入给定嵌套路由如下:
{
path: '/parent',
meta: { requiresAuth: true, isChild: false },
children: [
{ path: 'child', meta: { isChild: true }}
]
}
2
3
4
5
6
7
8
导航到/parent/child时,to.meta属性变为:
{ requiresAuth: true, isChild: true }
2
# # 16. css中scope变更 (opens new window) (opens new window)
使用::v-deep()
代替Vue2.x中的 >>>
和 /deep/
<style scoped>
/* deep selectors */
::v-deep(.foo) {}
/* targeting slot content */
::v-slotted(.foo) {}
/* one-off global rule */
::v-global(.foo) {}
</style>
2
3
4
5
6
7
8
9
10
11
# # 17. falsy属性转换策略 (opens new window) (opens new window)
在Vue2.x template模板中,属性为“falsy”值(undefined,null,false)时,会被“removeAttribute”,源码可看这里 (opens new window) (opens new window)。
在Vue3中,“falsy”中去掉了false
。当为false时,会作为attribute=false,当为undefined或null,跟2.x一致会removeAttribute。
# # 18. 异步组件api (opens new window) (opens new window)
新语法defineAsyncComponent
支持
import { defineAsyncComponent } from "vue"
// simple usage
const AsyncFoo = defineAsyncComponent(() => import("./Foo.vue"))
// with options
const AsyncFooWithOptions = defineAsyncComponent({
loader: () => import("./Foo.vue"),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})
2
3
4
5
6
7
8
9
10
11
12
13
14
# # 19. 移除内联模板api (opens new window) (opens new window)
冷门的api,这个api可以看Vue官方内联模板使用说明 (opens new window) (opens new window)
# # 20. emit事件数据校验 (opens new window) (opens new window)
增加新的 emits
选项api,可以对emit触发的事件数据,进行校验
const Comp = {
emits: {
submit: payload => {
// validate payload by returning a boolean
}
},
created() {
this.$emit('submit', {
/* payload */
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# # 21. Vue Test Utils提升异步流 (opens new window) (opens new window)
Vue单元测试时,允许使用await
语法触发re-render
const wrapper = mount(Component)
await wrapper.find('.element').trigger('click')
// 不再需要如下,在下一个事件循环中拿到dom值
// await wrapper.vm.$nextTick()
expect(wrapper.find('.finish').attributes('disabled')).toBeFalsy()
2
3
4
5
6
# # 22. router路由改动
路由新增router.push返回Promise值
,router.afterEach、router.onError也返回Promise值- 更方便的
动态增、删、查
路由信息,对应api:router.addRoute、router.removeRoute、router.hasRoute、router.getRoutes
相关rfcs:
- https://github.com/vuejs/rfcs/blob/master/active-rfcs/0033-router-navigation-failures.md
- https://github.com/vuejs/rfcs/blob/master/active-rfcs/0029-router-dynamic-routing.md