前言
vue3相对vue2是一个大的版本升级,数据劫持的api 从Object.defineProperty
变成Proxy
,对数组 的监听和对象的动态添加属性的响应式更加友好了。
然后vue3也推出了新的逻辑和语法,比如组合式api,reactive,ref,setup钩子等,今天总结下vue3相对vue2升级的点。
正文
组合式api
之前vue2是使用选项式api
,需要定义data,methods,watch,computed, 一部分逻辑有可能在methods,一部分逻辑在watch等等。
而vue3的组合式api
,可以把关联的逻辑写在一起,这样如果需要复用的时候,可以单独拎出来复用。
组合式api:
js
import { reactive, watch } from 'vue'
export default {
name: 'HomeView',
setup () {
// 以下是关联的逻辑
const info = reactive({ name: '答案cp3' })
watch(() => info.name, () => {
console.log('名字改变了')
})
const changeNameFn = () => {
info.name = 'cp3'
}
// 其它逻辑
return {
info,
changeNameFn
}
}
}
选项式api:
js
export default {
name: 'HomeView',
data () {
return {
info: { name: '答案cp3' }
}
},
watch: {
// ...可能有其它逻辑
'info.name' () {
console.log('名字改变了')
}
},
methods: {
// ...可能有其它逻辑
changeNameFn () {
this.info.name = 'cp3'
}
}
}
上面这个是简单的例子,如果我们组件的逻辑变得更复杂,选项式api的逻辑就会变得更分散,而组合式api的逻辑还是很直观的,因为它是一块一块的。
生命周期钩子
vue3推出了setup钩子,它是组合式api的入口,其它的生命周期钩子一般得在它内部执行。
vue3对比vue2,钩子函数都重命名了,请看表格:
vue3组合式api | vue2选项式api |
---|---|
- | beforeCreate |
- | created |
onBeforeMount | beforeMount |
onMounted | mounted |
onBeforeUpdate | beforeUpdate |
onUpdated | updated |
onActivated | activated |
onDeactivated | deactivated |
onBeforeMount | beforeDestroy |
onUnmounted | destroyed |
onErrorCaptured | errorCaptured |
它有2个参数props
,context
js
setup (props, context) {
// props: 父组件传的props
// context: 值有attrs,slots,emit, expose。
}
attrs等同于vue2的attrs
, slots相当于vue2的slots
,emit相当于vue2的emit
dispose是新增的,可以选择返回暴露哪些公共属性,当父组件访问时,只能访问这些属性。
js
setup (props, { expose }) {
expose({
// 属性
})
}
注意:attrs和slots是非响应式的
reactive/ref
vue3的setup钩子执行在beforeCreate
之前,没有this
,所以无法访问data的变量,同时vue3也不建议把变量定义在data中。
vue3重新定义了变量的语法,比较常见是reactive
和ref
。
reactive
reactive一般用来监听对象类型,如果不是对象类型,会报错
js
import { reactive } from 'vue'
const info = reactive({name:'答案cp3'}) // 正确
const name = reactive('答案cp3') // 错误❌
info.name = 'cp3' // 修改属性触发响应更新
info.age = 18 // 新增属性也能响应更新
定义后对对象的属性赋值修改会触发响应更新,如果对整个对象赋值,会让对象丢失响应式
js
import { reactive } from 'vue'
let info = reactive({name:'答案cp3'})
// 这个赋值会让info丢失响应式
info = {name: 'cp3'}
info.name = '答案' // 没有响应式了
如果需要整体赋值,应该再嵌套一层对象,让info成为嵌套对象的一个属性,或者使用下面说的ref
。
ref
大家一般会在基本数据类型
使用ref
,但是其实它也支持引用类型
,比如对象。
然后使用的时候需要加上.value
(模板上的语法不需要)。
js
<div @click="changeNameFn">{{ name }}</div>
import { ref } from 'vue'
const name = ref('答案cp3')
const changeNameFn = () => {
name.value = 'cp3'
}
return {
name,
changeNameFn
}
ref监听对象也是可以
js
<div @click="changeInfoFn">{{ info.name }}</div>
import { ref } from 'vue'
const info = ref({name: '答案cp3'})
const changeInfoFn = () => {
info.value = {name: 'cp3'} // 或者info.value.name = 'cp3' 都可以
}
return {
info,
changeInfoFn
}
reactive和ref的异同
reactive返回的是proxy
对象,ref返回的是RefImpl
对象。
packages/reactivity/src/reactive.ts
js
export function reactive(target: object) {
// ...省略
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
function createReactiveObject() {
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// ...省略
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy // 返回proxy对象
}
packages/reactivity/src/ref.ts
js
export function ref(value?: unknown) {
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow) // 返回RefImpl对象
}
虽然二者对象不一样,但是也有相通点,如果你是使用ref
监听对象类型 ,内部也是调用的reactive
。
js
// ref.ts
class RefImpl<T> {
// ...省略
constructor(
value: T,
public readonly __v_isShallow: boolean
) {
this._value = __v_isShallow ? value : toReactive(value)
}
// reactive.ts
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value
可以看到,会在RefImpl
实例化的时候调用toReactive
函数,在toReactive
函数中判断是否为对象,如果是就调用reactive
函数。
一般我们会在引用类型使用
reactive
,基本数据类型使用ref
(但是它也支持引用类型)
总结
以上就是vue3相对vue2升级的点,包括组合式api ,setup钩子函数 ,以及reactive和ref语法,vue3还有其它升级的点,欢迎大家去探索发现。
感谢大家的阅读。