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] } javascript 复制代码 export function isReadonly(value) { return !!value[reactiveFlags.IS_READONLY] } export function isReactive(value) { return !!value[reactiveFlags.IS_REACTIVE] }
这样代码就优化了一下
写在最后
vue3的源码写的很巧妙,响应式原理写的也很好,希望能够学习到这种思想在编码过程中有所收获