isReactive以及isReadonly是用来判断当前对象是否是reactive对象,readonly对象的
这个前提是什么,是数据的类型是对象对吧,首先要是一个对象,另外对一个对象它如果内部还嵌套的有对象,那么我们内部的对象也应该是相应的reactive或者readonly,我们先来实现以下isReactive以及isReadonly
目录
isReactive与isReadonly
isReactive和isReadonly最大区别是啥,一个可改,一个不可改 而且reactive需要收集依赖,而readonly不需要,那么我们根据这点来写代码,那么我们判断一下是不是就可以了
我们首先需要把之前写的reactive以及readonly代码给优化一下
import { track, trigger } from './effect'
export function reactive(raw) {
return new Proxy(raw, {
get(target, key) {
const res = Reflect.get(target, key)
//todo收集依赖
track(target, key)
return res
},
set(target, key, value) {
const res = Reflect.set(target, key, value)
//todo触发依赖
trigger(target, key)
return res
}
})
}
export function readonly(raw) {
return new Proxy(raw, {
get(target, key) {
const res = Reflect.get(target, key)
return res
},
set(target, key, value) {
return true
}
})
}
优化后
import { track, trigger } from './effect'
export function reactive(raw) {
return new Proxy(raw, {
get,
set
})
}
export function readonly(raw) {
return new Proxy(raw, {
get: getReadonly,
set(target, key, value) {
return true
}
})
}
const get = createGetter()
const set = createSetter()
const getReadonly = createGetter(true)
function createGetter(isReadonly = false) {
return function get(target, key) {
const res = Reflect.get(target, key)
if (isReadonly) {
return res
}
track(target, key)
return res
}
}
function createSetter() {
return function set(target, key, value) {
const res = Reflect.set(target, key, value)
trigger(target, key)
return res
}
}
我们把get里面相同的逻辑抽出来,然后定义了一个高阶函数,来返回一个get,我们给高阶函数传的参数可以在内部做判断
因为我们的readonly与reactive在track的时候区分的很明显,那么我们是不是可以继续在get上做文章也来区分他俩,如何触发track,是不是得触发get 如何触发get 是不是得读属性,想到这里我们通过读不同的属性,我们给不同的返回值区分他俩是不是就可以了,来实现一下
function createGetter(isReadonly = false) {
return function get(target, key) {
const res = Reflect.get(target, key)
if (key === reactiveFlags.IS_REACTIVE) {
return isReadonly
} else if (key === reactiveFlags.IS_READONLY) {
return !isReadonly
}
if (isReadonly) {
return res
}
track(target, key)
return res
}
}
function createSetter() {
return function set(target, key, value) {
const res = Reflect.set(target, key, value)
trigger(target, key)
return res
}
}
export function isReadonly(value) {
return value[reactiveFlags.IS_READONLY]
}
export function isReactive(value) {
return value[reactiveFlags.IS_REACTIVE]
}
我们定义了一个枚举类,来区分reactive与readonly,当我们调用isReactive的时候我们通过读枚举项的值,也就调用了get 而读到的属性也就是key ,我们用key来区分一下,就能够得到最后的效果
解决深层嵌套
我们上面实现的isReactive与isReadonly只是在最外层实现了,没有实现深层监听, 我们看一下单测,需要将user身上的name也给监听到
这个时候我们想一下,啥情况要这样做,肯定是内部属性也是一个对象,所以我们要先判断一下是否是个对象,另外如果一环套一环的,我们是不是需要一层一层的判断,那么我们就可以用递归的方式来处理
function createGetter(isReadonly = false) {
return function get(target, key) {
const res = Reflect.get(target, key)
if (res !== null && typeof res === 'object') {
return isReadonly ? readonly(res) : reactive(res)
}
if (key === reactiveFlags.IS_REACTIVE) {
return isReadonly
} else if (key === reactiveFlags.IS_READONLY) {
return !isReadonly
}
if (isReadonly) {
return res
}
track(target, key)
return res
}
}
到此我们的深层嵌套的问题也解决了
优化代码
我们这里把相同的逻辑抽出来,并且在外层来接收函数调用,为什么不在内部,比如get: createGetter() 而是写在外面,只是因为,当我们每次调用get时 函数都会被初始化一次,放在外面,他只会初始化一次,节省性能了
另外注意点,我们这样传的参数是对象当然没问题,要不是对象是不是就是undefied了,所以我们需要转转义一下
export function isReadonly(value) {
return value[reactiveFlags.IS_READONLY]
}
export function isReactive(value) {
return value[reactiveFlags.IS_REACTIVE]
}
export function isReadonly(value) {
return !!value[reactiveFlags.IS_READONLY]
}
export function isReactive(value) {
return !!value[reactiveFlags.IS_REACTIVE]
}
这样代码就优化了一下
写在最后
vue3的源码写的很巧妙,响应式原理写的也很好,希望能够学习到这种思想在编码过程中有所收获