为了对即将到来的vue3有个全面的认识,通读Vue3 rfcs (opens new window)(意见修改稿)是有必要的。但原版英文内容比较长,通读时间比较耗时。这里笔者根据原文内容总结输出,方便大家对Vue3细节改动有个全局的认识。更多详细设计请在每个章节链接进去查看。
核心变动,增加setup选项
,更多动机和设计看官方教程 (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>
v-slot
v-slot
简写为#
统一为this.$slots
,并且暴露为funciton函数。(ps:集合了this.$slots和this.$scopedSlots各自特点)相关rfcs:
指令参数支持动态设置
<div v-bind:[key]="value"></div>
<div v-on:[event]="handler"></div>
v-model:arg
语法 代替:arg.sync
update:modelValue
,而不是以前的事件名:input。(ps:主要还是统一上一条变动)相关rfcs:
<!--模版里不再有.sync语法-->
<MyComponent v-model:title="title" />
关键点:
函数式组件不再是对象,而变成类React函数式
// 基本展示
import { h } from 'vue'
const FunctionalComp = (props, { slots, attrs, emit }) => {
return h('div', `Hello! ${props.name}`)
}
所有挂载在Vue对象的方法,都单独出去了,比如Vue.nextTick、Vue.observable
关键点:
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')
]
)
}
}
创建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')
自定义指令跟vue hook命名一致
const MyDirective = {
beforeMount(el, binding, vnode, prevVnode) {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {}, // new
unmounted() {}
}
因为KeyboardEvent.keyCode (opens new window)已经被废弃,故在vue3中移除
使用方法或computed
代替filter
<!-- before -->
{{ msg | format }}
<!-- after -->
{{ format(msg) }}
from/to名称对称,更好理解
v-enter-from
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;
}
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter-from 会替换为 my-transition-enter-from
以前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')
Vue3不再提供事件发布接口,如果有发布事件需要,可以使用mitt (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 }}
]
}
导航到/parent/child时,to.meta属性变为:
{ requiresAuth: true, isChild: true }
使用::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>
在Vue2.x template模板中,属性为“falsy”值(undefined,null,false)时,会被“removeAttribute”,源码可看这里 (opens new window)。
在Vue3中,“falsy”中去掉了false
。当为false时,会作为attribute=false,当为undefined或null,跟2.x一致会removeAttribute。
新语法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
})
冷门的api,这个api可以看Vue官方内联模板使用说明 (opens new window)
增加新的 emits
选项api,可以对emit触发的事件数据,进行校验
const Comp = {
emits: {
submit: payload => {
// validate payload by returning a boolean
}
},
created() {
this.$emit('submit', {
/* payload */
})
}
}
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()
路由新增router.push返回Promise值
,router.afterEach、router.onError也返回Promise值动态增、删、查
路由信息,对应api:router.addRoute、router.removeRoute、router.hasRoute、router.getRoutes相关rfcs: