Vue 团队发布 v3.0 版本已经有一段时日了,作为一次主版本更新,Vue3 带来了全新的开发体验和更优的性能提升,虽然目前周边的生态插件,例如 Vue-Router、Vuex 等还不够完善,但也有不少用户和企业将其用在了生产环境,主流 UI 组件库像 Ant-Design Vue、ElementUI、Vant 等也陆续推出了适配 Vue3 的稳定版本。也就是说,现在开始上手 Vue3 还不算太晚,这篇文章将向你介绍 Vue3 的新特性以及主要更新的内容。
使用 TypeScript 重构整个框架
TypeScript 是 JavaScript 的一个超集,它提供了对 ES6 的支持,可以编译成纯粹的 JavaScript。这次更新包括响应式系统、模板编译和 vDom 在内,所有代码都使用 TypeScript 进行了重写,提升性能的同时,还减少了一定的体积。不过对于不太熟悉 typeScript 又想要阅读源码的同学来说可能需要点成本。
使用 Proxy 实现响应式
为了实现更好的性能和深度监听,这次采用了 Proxy 去实现响应式,弥补了 Vue 2.x 中 Object.defineProperty 的不足:
- 不能监测属性的添加、删除动作
- 不能监测数组下标的修改和对于 length 修改
- 不支持 Map、Set、WeakMap 和 WeakSet Vue2.x中,如果 Data 的层级较深的话,使用defineProperty 会有明显的卡顿,Proxy 很好的规避了这个问题;Proxy可以深度监听且性能更好,也可以监听数组的变化,不过,Proxy 实现响应式是无法兼容所有浏览器的,也无法支持 Polyfill。
源码体积优化、编译优化
这次更新移除了一些冷门的 Feature,例如 Filter、Inline-template 等,因此节省了不少体积,此外,还引入 Tree-shaking 技术减少打包后的体积大小;在编译方面,将从模板中取出的静态节点和数据等提升到渲染函数之外,避免每次渲染都要重新创建这些对象,从而大大提升内存使用率,并减少垃圾回收的频率。
新的语法 API
如果你是 Vue2.x 的开发者,肯定在开发较大的组件有过这样的不舒服——上下横跳真xx难受撒,所以如果想要复用组件的某一部分代码,而且想要更好的支持 typeScript,那么Vue 3 带来的 Composition API 就很好的解决了这个问题;优化 Vue 2.x 中复杂逻辑带来的上下反复跳动式的开发体验,使开发者可以更好的优化逻辑和逻辑复用;新增统一入口 setup,以及全面替换过的生命周期钩子。
支持多个片段 - Fragment
在 Vue2.x 中创建一个组件,要求每个模板中必须有一个根节点,否则会提示异常,于是我们不得不这样做:
<template>
<div>
<p>Vue2.x</p>
<p>Vue3</p>
</div>
</template>
不过可以通过引入类似vue-fragments
的第三方库去解决这个问题,但在 Vue3 中,我们开发的时候可以直接使用多个片段:
<template>
<p>Vue2.x</p>
<p>Vue3</p>
</template>
不仅提升了开发体验,还减少了许多不必要的元素。
传送门 - Teleport
Teleport 可以将你的组件挂载到指定元素底部,还是有很多常见的使用场景的,在一些嵌套层级比较多的组件中,为了利于组件维护,将样式和 DOM 脱离嵌套层级,挂载到指定元素底部。需要注意,虽然脱离了原有的组件中的嵌套层级,但它们仍属于父子组件的关系,Teleport 接收两个参数:
- to:有效的 HTML 元素,或其 id 、class选择器;
- disabled:设置是否禁用;
新的生命周期钩子 - Lifecyle Hooks
Vue 3 新的生命周期钩子只能在 setup 的组件实例中使用,在其它地方调用会报错,相比较于 Vue 2.x 的生命周期,对比如下:
Vue2.x | Vue3 |
---|---|
beforeCreate | setup |
created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
Vue 3 也新增了两个新的钩子函数: onRenderTracked:检查哪个 Reactive 对象属性或一个 ref 作为依赖被追踪。当 Render 函数被调用时,会检查哪个响应式数据被收集依赖。 onRenderTriggered:当执行 Update 操作时,会检查哪个响应式数据导致组件重新渲染。
setup-Composition API 的大门
setup 作为 Composition API 的大门,除几个辅助函数之外,其它的 Composition API 都得在 setup 中才能使用,在初始化 Props 之后,setup 即被调用,在生命周期中,setup 会在 beforeCreate 之前调用。
setup定义
function setup(props: Data, context: SetupContext): Data
interface Data {
[key: string]: unknown
}
interface SetupContext {
attrs: Data
slots: Slots
emit: (event: string, ...args: unknown[]) => void
}
新的插槽指令 v-slot
实际上这个指令在 Vue 2.6 的时候就开始引入了,用来表示具名插槽和默认插槽,但是 slot-scope 会随着层级的增加而不能清晰的反映出作用域到底是来自于哪一个插槽,于是在 Vue 3 中通过指令参数来表示插槽的名称。
<!-- 默认插槽-->
<div v-slot="{ msg }">
{{ msg }}
</div>
<!-- 具名插槽 -->
<div>
<template v-slot:one="{msg}"> {{ msg }} </template>
</div>
动态插槽名
动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:
<div>
<template v-slot:[slotName]></template>
</div>
插槽指令的缩写
和 v-bind 和 v-on 相似,缩写只有在存在参数时才生效,这就意味着 v-slot 没有参数时不能使用 #=,对于默认插槽,可以使用 #default 来代替 v-slot。
<!-- 缩写 -->
<div>
<template #header="{ msg }"></template>
<template #footer></template>
</div>