vue3新特性
更好的支持typescript
使用
proxy
+Reflect
代替defineProperty
实现响应式由于
proxy
和Reflect
的方法一一对应,由proxy
监听拦截对应操作,Reflect
来完成并返回对应的默认行为,- 将条件判断和实际行为优雅的区分,
proxy
性能更好- 数组也可以深度响应(
proxy
和Reflect
都可以操作数组)
重写虚拟dom实现和Tree-Shaking
vue3组件的模板可以不只有一个根标签
将不会支持 IE11
vue-cli3 4.5.0以上
生命周期
除了beforeDestroy
和destroyed
已经改名了不能用, vue2的其他生命周期在vue3同样可用,但vue3都有对应的组合API可以在setup
中代替。vue2和vue3生命周期对比: 以下右边的生命周期也可以去掉on
可以跟vue2一样option
方式定义生命周期
beforeCreate
和created
=>setup
beforeMount
=>onBeforeMount
mounted
=>onMounted
beforeUpdate
=>onBeforeUpdate
updated
=>onUpdated
beforeDestroy
=>onBeforeUnmount
destroyed
=>onUnmounted
errorCaptured
=>onErrorCaptured
经过测试同样生命周期定义在setup中都比vue2的方式快,优先级更高
新增renderTracked
和renderTriggered
开发环境的调试生命周期
renderTracked
响应数据开始跟踪虚拟dom渲染时触发,type
一般是get
renderTriggered
响应数据重新渲染组件时触发,type
一般是set
这两个生命周期参数对象主要有三个属性:
key
数据的键target
数据的目标对象type
操作类型
与
customRef
中的track
和trigger
两个函数有渊源
Composition API
vue导出的全局API函数: 如import { 函数 } from 'vue'
createApp
传入组件,创建应用级实例对象defineComponent
定义组件defineAsyncComponent
定义懒加载的异步组件,vue2中的()=>import(path)
方式已经废弃
响应性API
setup
组件方法optionsetup
方法有两个参数:props
、context
。props
或者context.emit
都可以接收、调用父组件方法(包括vue2)
props
, 从父组件传递来的所有props接收的属性context
对象内有props
(props接收)、attrs
(未被接收)、emit
、slots
(传入的插槽内容对象)、
- 在所有生命周期之前执行,
this
获取不到组件实例 - 组合API的入口,所有组合API函数在这调用,只初始化时执行一次
- 若返回一个对象,该对象的属性方法都合并到组件模板的渲染上下文(与data数据合并成为组件对象属性,与methods中方法合并成为组件方法 ,
setup
优先级更高)
ref
定义基本类型响应式数据。setup中定义的普通数据不是响应式的,需要通过此函数处理,将返回对象用到模板上。通过操作此函数返回值的value
来响应式修改。如果传入的是对象,内部使用
reactive
代理处理,但也要操作value
- 组件或标签
ref
属性值可以为ref
类型响应式数据的变量名,那么页面挂载后该数据的 value
就是该组件实例或标签元素
- 组件或标签
shallowRef
定义值类型响应式数据,并且传入对象也不用reactive
处理,也就是说所有类型数据只有内存地址修改才能响应triggerRef
手动触发一次与shallowRef
关联的响应效果(包括使对象深层响应一次)。1
//用watchEffect监听一个shallowRef后,由于shallowRef不能监听对象内层数据,在修改内层数据后调用`triggerRef(shallowRef)`手动触发监听
unref
返回ref数据的内部value
值,不是ref数据则返回数据本身。操作其返回的值就不用每次去ref.value
了toRef
为reactive
创建的响应式对象的属性创建一个ref对象,创建的ref对象跟源属性响应式连接(变化同步):toRef(proxyObj, prop)
常来为复合函数提供响应式对象toRefs
将响应式对象转为为普通对象,将此对象内的属性都处理为对应的ref(与源对象属性响应性链接),方便响应数据解构(解构返回的普通对象)customRef
创建自定义的ref,可控制其依赖项跟踪及更新。参数为一个工厂函数: 接收track
和trigger
函数作为参数,并返回带有get
和set
的对象。 如可以实现一个带有响应防抖的响应数据:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23setup(p, c) {
function debounceRef(value: string, delay: number) {
let timer: number
return customRef((track: () => void, trigger: () => void) => {
return {
get() {
track()
return value
},
set(val: string) {
clearTimeout(timer)
timer = setTimeout(() => {
value = val
trigger()
}, delay)
}
}
})
}
return {
refP: debounceRef('111', 200)
}
},get
中调用track
告诉Vue开始追踪这个数据set
中调用trigger
通知view更新视图reactive
定义对象的响应式数据,深层的影响所有嵌套的属性,返回对象的响应式代理器shallowReactive
跟reactive
一样,但只是第一层属性响应式,深层的都是普通数据readonly
和shallowReadonly
返回对象的代理数据,该代理数据只读。区别是shallowReadonly
仅对第一层只读,深层的不限制。toRaw
返回被reactive
或readonly
代理数据的源对象,包括shallowReactive
和shallowReadonly
markRaw
标记一个对象,使其永远不会被响应式代理。返回对象本身computed
返回ref对象,使用返回的ref
数据的value
。参数可以是两种:- 返回计算数据的
getter
函数 - 同时包括
get
和set
的对象,不能只有一个
- 返回计算数据的
watch
监听响应式数据变化- 监听单个数据,可以是
getter
函数,也可以是响应式数据(ref
或proxy
代理对象)。如果是getter
则处理函数的参数就是getter
的返回值。 - 要监听简单类型数据,第一个参数必须使用getter函数返回该数据
- 对于对象、数组等复杂类型,新旧值指向的是同一个内存地址,没有深拷贝
- 回调新增第三个参数
onCleanup
是一个函数,调用后它的回调会在第二次触发watch之前执行
- 监听单个数据,可以是
监听多个数据用数组。此时处理函数的参数会是两个数组,数组有与数据相同数量元素,分别对应数据的变化后和变化前的值(对象属性都会成为变化后的值,猜测由于引用类型影响)
- 第三个参数可传配置对象
option
,其中:immediate: boolean
初始自动执行一次监听回调deep
深层监听数据内部属性,ref
响应的对象不会深层监听,需要加deep
- 第三个参数可传配置对象
watchEffect
只需要接受一个处理函数- 会自动监听处理函数内使用到的响应数据并触发这个函数
初始自动执行一次,相当于
watch
的immediate
。provide
和inject
为所有后代组件提供数据用法: 祖先组件
provide(key, value)
, 后代组件inject(key)
。一般都在setup中使用provide
也支持响应式数据,而vue2方式的provide
对响应式数据有如下局限:provide
内部this
未指向组件实例,需要将provide
定义为方法,return
一个对象,该对象内的this
才指向组件组件实例。不用响应的数据直接将
provide
定义为对象,此时内部this
不会指向组件实例提供响应式对象包裹响应式数据才能在子组件响应,也就是提供给后代的数据内存地址不能更改,只能响应数据内部。vue3可随意修改保持响应
记得操作ref对象都要操作其
value
属性,包括ref
对对象隐式使用reactive
处理的情况(只要不是直接用reactive
)
getCurrentInstance
可以在开发环境在setup
中通过返回的ctx
属性获取到当前组件实例,但是生产环境不行。有些地方说生产环境proxy
属性可以获取,但是经尝试根本无法使用
响应式实现
1 | //手写reactive实现原理: |
1 | //ref可以用对象的get、set实现,深层数据用reactive代理 |
类型判断
vue3可以用全局API判断数据的响应式类型,返回boolean
isRef
是否是ref数据,内部通过判断__v_isRef
属性。包括shallowRef
isReactive
是否是reactive
创建的代理,内部在get
中返回了__v_isReactive
作为标识。包括shallowReactive
isReadonly
是否是readonly
创建的代理,内部在get
中返回了__v_isReadonly
作为标识。包括shallowReadonly
isProxy
是否是reactive
或readonly
创建的代理,内部判断是否满足isReactive
或isReadonly
自定义Hook函数
使用vue3组合API封装的可复用功能函数,作用类似于混入,清楚功能代码来源,更清楚易懂。方式就是使用vue3的全局函数封装为一个功能函数,可返回功能性响应式数据,在任意组件的setup中调用获取并用到模板中。
内置指令
v-model
v-model:xx="yy"
使用
xx
作为modal
选项中的prop
修饰符
- 自定义修饰符
capitalize
(vue3新增) lazy
输入框发生blur
才触发number
将字符串转为number
trim
去掉首末空格
- 自定义修饰符
在vue3当中,
v-model
对组件默认的prop
不再是value
,而是modelValue
默认的
event
不再是input
,而是update:modelValue
内置组件
Fragment
片段,在vue2中必须有一个根标签,而vue3内部将所有标签包裹在Fragment
虚拟元素中不再必须根标签- 减少了层级和内存占用
Teleport
瞬移,可将该组件包裹的元素放到页面任意祖先级元素位置下,而它的逻辑还是在组件内实现to
指定目标祖先元素的选择器,不可以是当前组件内元素。若存放
teleport
的组件作为子组件使用,to
也不能是使用此子组件的父组件内的元素disabled
是否禁止瞬移
Suspence
可以在等待异步组件时渲染一些后备内容进行平滑过渡1
2
3
4
5
6
7
8<suspence>
<template #default>
<async-component/>
</template>
<template #fallback>
<h2>后备内容</h2>
</template>
</suspence>我尝试时无效,官方文档也搜不到,可能只是实验性API,或者是脚手架版本问题。过一段时间再看
模块声明
若vue3中使用了ts,引入没有声明文件的js
文件会报错,解决方式: 在shims-vue.d.ts
中声明对应模块信息
Typescript支持
defineComponent
全局方法定义组件可以使ts正确推断 Vue 组件选项中的类型可以对
data
返回的数据进行类型断言,对计算属性返回值进行类型声明不用在
setup
中声明prop类型,是从定义props
选项进行类型推断:对定义了
type
的prop
验证,需要将类型提供给typescript
,需要用PropType
强转构造函数1
2
3
4
5//MSG是定义的msg对象的interface
import { PropType } from 'vue'
msg: {
type: Object as PropType<() => MSG>
}withDefaults
对props绑定默认值ref
和reactive
等调用时可以传递泛型声明类型:1
ref<string | number>('2020')
directive
、component
这些全局API在main.js/ts中使用必须要在mount
挂载前
vuex
$store
需要使用vue的
ComponentCustomProperties
并定义state
接口的约束,否则在组件中使用this.$store
没有类型推断1
2
3
4
5
6
7
8
9
10
11
12
13import { ComponentCustomProperties } from "vue";
import { Store } from "vuex";
import { state } from "@/store/index";
declare module "@vue/runtime-core" {
interface state {
name: string;
value: number;
}
interface ComponentCustomProperties {
$store: Store<state>;
}
}useStore
在setup中需要使用
vuex
中的useStore
方法获取store,但是默认也没有类型支持,最好的方法是自定义useStore
方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//利用了provice/inject特性
//setup获取store都从此文件引入useStore
import { createStore, useStore as baseUseStore, Store } from "vuex";
import { InjectionKey } from "vue";
export const key: InjectionKey<Store<State>> = Symbol();
export function useStore(): Store<State> {
return baseUseStore(key);
}
export interface State {
name: string;
value?: number;
}
//createStore是泛型,可传入state类型
export default createStore<State>({
state: {
name: "xx",
},
mutations: {},
actions: {},
modules: {},
});模块类型支持
使用
Module
泛型约束1
2
3
4
5
6
7
8
9
10
11//State - 当前子模块state类型
//RootState - 根state类型
import type { Module } from "vuex"
export const moduleA: Module<State, RootState> {
state: {
name: xx-xx
},
mutations: {},
actions: {},
modules: {},
}
有一说一,vuex4.x对ts支持真不太行。很多情况下vue3完全可以用reactive组合api来管理状态,或者reactive配合 provide、 inject 来实现状态管理
vue-router
路由规则项
routes
每一项中,meta
默认就有类型支持,但对于其他自定义的配置项需要使用下例RouteRecordRaw
扩展的类型来约束routes
:1
2
3
4
5import { RouteRecordRaw } from "vue-router"
type CustomRouteRecordRaw = RouteRecordRaw & {
自定义的项...
}
const routes: Array<CustomRouteRecordRaw> = [...路由规则]
axios
axios请求函数也支持通过泛型来定义返回res.data
的数据
.env环境
- 在vite中可通过
import.meta.env
来获取.env文件中的环境变量。除开断言外,环境变量的类型支持可用如下方法:- 创建.d.ts声明文件
- 在声明文件中声明
ImportMetaEnv
接口
与vue2比较
prop
自动继承- vue2中非
prop
属性如果不设置inheritAttrs: false
,会自动给到组件根节点 - vue3中由于可以设置多个根节点(
fragments
),同样情况对于有多个根节点时会出现警告。必须手动在至少一个根节点上绑定
- vue2中非
emit事件
vue3中触发父组件的自定义事件要先预设
emits
选项:1
2
3
4
5
6
7
8
9
10
11//数组
emits: [e1, e2]
//对象 验证参数
emits: {
e1: null, //null表示不验证
e2: (...args) => {
//参数为触发时的实参
//返回true验证通过
return true
}
}
异步组件
vue3提供
defineAsyncComponent
函数,返回异步组件对象用于注册,异步组件打包时会拆分为独立chunk,该函数参数:1
2
3
4
5
6//工厂函数,要返回promise,通常配合import
cont comp = defineAsyncComponent(() => import(path))
//配置对象
const comp = defineAsyncComponent({
loader: () => import(path)
})
移除了
$children
vue3+ts代码校验
vue3+ts+eslint+prettier
.eslintrc.js
1 | module.exports = { |
.prettierrc.js
1 | module.exports = { |
setting.json
1 | { |
- 本文作者: MR-QXJ
- 本文链接: https://mr-qxj.github.io/2021/09/23/框架/vue3/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!