vue3新特性简介
Compiler 原理篇
静态 Node 不再作更新处理( hoiststatic->SSR 优化)
静态绑定的 class,id 不再作更新处理
结合打包标记 Patchflag,进行更新分析(动态绑定)
事件监听器
Cache
缓存处理(cacheHandlers)hoiststatic
自动针对多静态节点进行优化,输出字符串
vue2 中代码复用方法,如: Mixin, Filters 都有缺陷
- Mixin(命名空间冲突、逻辑不清晰、不易复用)
- scoped slot 作用域插槽(配置项多、代码分裂、性能差)
- vue2 对 TS 支持不充分
首先,先来看一些小的变化
生命周期更名:
destroyed
–>unmounted
beforeDestroy
–>beforeUnmount
data 选项始终声明为函数
删除
$on
,$off
和$once
API删除
Filters API
,改用method
和computed
替换v-on
不再支持使用数字(即keyCodes
)作为修饰符,config.keyCodes
不再受支持在
v-enter
过渡类已重命名为v-enter-from
和v-leave
过渡类已更名为v-leave-from
接下来看一些 Composition API 的更改:
为什么要用 Composition API?
组合式 API + 函数式编程(复杂组件逻辑进行分离)
组件间逻辑共享
Vue3.0 带来的变化
- 性能提升 1.3 ~ 2x
- TS 支持,新增: Fragment、 Teleport、 Suspense
- 按需加载(配合 vite) &
组合 API
Fragment
: 不受根节点限制,渲染函数可接收 Array
Teleport
类似 Portal
,随用随取,eg. 弹窗,ActionsSuspense
嵌套的异步依赖,eg. async setup()
vue2 对于复杂逻辑组件,在后期变得无法维护。逻辑被拆分成:
- components
- props
- data
- computed
- methods
- 生命周期的方法
Scaffold
vite:
1 | npm init vite-app hello-vue3 |
vue-cli:
1 | npm install -g @vue/cli |
注:尤大写的 vite 捆绑了 rollup 进行打包,而且具有以下特性:
- 快速启动冷服务器
- 即时热模块更换(HMR)
- 真正的按需编译
- 更详细的日志信息
v-for Array Refs
v-for 循环绑定的 ref 可以更加灵活,且定义非常方便
1 | <template> |
defineAsyncComponent
定义异步组件
1 | import { defineAsyncComponent } from "vue"; |
Custom Directives
v-for 循环绑定的 ref 可以更加灵活,切定义非常方便
bind: 一旦该指令被绑定到元素时执行。仅执行一次。
inserted: 在将元素插入父 DOM 中时执行。
update: 元素更新时调用此挂钩,但是子组件尚未更新。
componentUpdated: 组件和子组件更新后,将调用此挂钩。
unbind: 删除指令后将调用此钩子。也仅调用一次。
bind 更名为 beforeMount
inserted 更名为 mounted
beforeUpdate: 这在元素本身更新之前被调用,就像组件生命周期挂钩一样。
update 已删除
componentUpdated 更名为 updated
beforeUnmount 与组件生命周期挂钩类似,这将在卸载元素之前立即调用。
unbind 更名为 unmounted
Data Option
合并 data 来自 mixin 或扩展的多个返回值时,合并现在较浅而不是较深(仅合并了根级属性)。
1 | const Mixin = { |
Fragments
组件现在可以具有多个根节点!但是,这确实需要开发人员明确定义属性应在何处分发。
1 | <template> |
Global API
新增 createApp 返回一个应用程序实例,现在可以将全局更改 Vue 行为的所有 API 移至应用程序实例。
1 | import { createApp } from "vue"; |
2.x Global API | 3.x Instance API |
---|---|
Vue.config | app.config |
Vue.config.productionTip | 已移除 |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
key attribute
1 | <!-- key 应该将放在 --> |
v-model
在自定义组件上使用时,v-model
、prop
和 event
的默认名称已更改:
prop value
-> modelValue
event input
-> update: modelValue
v-model
现在可以在同一组件上进行多个绑定;v-bind
的 .sync
修饰符和组件 model 选项已删除,并替换为 v-model 的一个参数;
添加了创建自定义 v-model
修饰符的功能;
1 | <ChildComponent v-model="pageTitle" /> |
1 | // ChildComponent |
最后,我们再来看一些重大的变化
reactive
Vue3 响应式实现原理是通过 ES6 的 Proxy 实现的,但是对于 IE 浏览器,Vue3 也使用了 Object.defineProperty。
vue3 将 Vue.observable()重命名为 reactive,并提供了单独的分离。
1 | import { reactive } from "vue"; |
ref
对于一个独立的原始值,Vue3 也提供了对应的响应式 API。
- ref 包裹的变量(可以是任意类型)都是深度响应式的
- 通过 .value 取值和赋值
- 顶层 property 在 template 中自动解包,不需要 .value 取值
1 | import { ref } from "vue"; |
1 | // 解包过程仅作用于顶层 property,访问深层级的 ref 则不会解包 |
响应性语法糖
基于:
vue@^3.2.25 @vitejs/plugin-vue@^2.0.0
1 | // vite.config.js |
取值不需要
.value
1 | <script setup> |
toRefs
ES6 解构会破坏响应式,对于这种情况可以使用 toRefs 去避免。
1 | import { reactive, toRefs } from "vue"; |
readonly
有时我们想避免响应式更改,可以使用 readonly。
1 | import { reactive, readonly } from "vue"; |
computed
有时我们需要依赖于其他状态的状态,可以通过计算属性来处理的。
- 默认返回 readonly 的 ref 值,需要 .value 获取
- 可设置 writable 方式
1 | const count = ref(1); |
watch 和 watchEffect
区别:
- watch 只追踪明确侦听的源。它不会追踪任何在回调中访问到的东西。另外,仅在响应源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
- watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式 property。这更方便,而且代码往往更简洁,但其响应性依赖关系不那么明确。
watch
- 默认懒监听, 第一个参数要是个响应式变量,如果监听数值变化,用 getter
- watch 的第二个参数为 callback,callback 参数第三个为 cleanup 函数,在下次变更前调用
- watch 的第三个参数为配置项,包括
- immediate : watch 创建后立马调用,oldValue 此时为 undefined
- deep: 深度监听,用于监听第一个参数为 getter 的 响应式对象, 用于大型数据结构时,开销很大
监听一个 getter
1 | // 监听一个getter,正常值 |
1 | // 用 getter 函数 |
错误:
这不会正常工作,因为是向 watch() 传入了一个 number
1 | watch(state.count, (count) => { |
监听一个 getter 深层响应的对象
1 | const state = reactive({ count: 0 }); |
直接监听深层响应的对象
自动默认 deep 为 true
1 | const state = reactive({ count: 0 }); |
监听一个 ref
1 | const count = ref(0); |
监听多个 refs
1 | watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { |
flush
pre
|post
|sync
默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。
在侦听器回调中能访问被 Vue 更新之后的 DOM,需要设置 post
1 | watch(source, callback, { |
onTrack / onTrigger
1 | watch(source, callback, { |
watchEffect
为了监听响应式更改,可以使用该 watchEffect 方法,它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。它会返回一个停止函数,通过显式调用可以停止侦听。
特性:
- 立即执行
- 第一个参数是执行函数,函数的第一个参数是清除函数(下一次触发执行函数时调用)
- 第二个参数为配置项
1 | const count = ref(0); |
清除函数使用
1 | const id = $ref(1); |
侦听器必须用同步语句创建
如果用异步回调创建一个侦听器,则不会绑定到当前组件上,必须手动停止它
1 | <script setup> |
setup
setup 将可重复部分及其功能提取到可重用的代码段中。
setup 选项接受 props 和 context 的函数,返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。。
1 | <template> |
Provide / Inject
- 在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 时来定义每个 property。
- provide 函数允许你通过两个参数定义 property:
- property 的 name ( 类型)
- property 的 value
1 | // src/components/MyMap.vue |
在 setup() 中使用 inject 时,还需要从 vue 显式导入它。一旦我们这样做了,我们就可以调用它来定义如何将它暴露给我们的组件。
- inject 函数有两个参数:
- 要注入的 property 的名称
- 一个默认的值 (可选)
1 | // src/components/MyMarker.vue |