Vue3 中的 getter 和 setter 源码简解

2,533 阅读2分钟

关于 Getter

vue3 中的所有 getter 都源自于一个名为 createGetter 的函数,下面试该函数的源代码

function createGetter(isReadonly = false, shallow = false) {
  return function get(target: object, key: string | symbol, receiver: object) {
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly 
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (
      key === ReactiveFlags.RAW && 
      receiver === 
        (isReadonly
          ? (target as any)[ReactiveFlags.READONLY]
          : (target as any)[ReactiveFlags.REACTIVE])
    ) {
      return target
    }

    const targetIsArray = isArray(target)
    if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
      return Reflect.get(arrayInstrumentations, key, receiver)
    }

    const res = Reflect.get(target, key, receiver)

    if (
      isSymbol(key)
        ? builtInSymbols.has(key)
        : key === `__proto__` || key === `__v_isRef`
    ) {
      return res
    }

    if (!isReadonly) { 
      track(target, TrackOpTypes.GET, key)
    }

    if (shallow) {
      return res
    }
    if (isRef(res)) {  
      // ref unwrapping, only for Objects, not for Arrays.
      return targetIsArray ? res : res.value
    }

    if (isObject(res)) { 
      // Convert returned value into a proxy as well. we do the isObject check
      // here to avoid invalid value warning. Also need to lazy access readonly
      // and reactive here to avoid circular dependency.
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}

这个函数对输入的数据的处理流程很清晰,基本是从上到下的流程函数,下面是简要分析:

第一步

接收 isReadonly(只读) 和 shallow (浅转换为 Reactive---> shallowReactive)两个参数。参数表示生成不同类的 getter

第二步

接下来是一系列的判断输入,返回对应的值。

获取当前 target 的类型

开始的判断是关于类型的判断(ReactiveFlags.IS_REACTIVE[响应式数据] 和 ReactiveFlags.IS_READONLY[只读数据])。

获取源对象

接下来是获取源对象,也就是调用 toRaw() 方法时需要进行的判断,获取的值是转换响应式之前的源对象。

toRow 源码和详解

export function toRaw(observed) {
  return (
    (observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
  )
}

toRaw 是一个简单的迭代函数,直到传入的对象不存在 ReactiveFlags.RAW 为止,就获取到了想要的源object。其实这里只需要直到,一般情况下 proxyhandlerget 函数的参数 target 指的就是 proxy 的第一个参数,所以上面的 createGetter 的判断是 获取 ReactiveFlags.RAW 时直接返回 target

第三步

关于数组的几个特殊方法的处理

具体的代码分析看这里

第四步

接下来是一系列的判断代码。这一系列代码都是常规的获取 key 对应的值的判断。

使用

const res = Reflect.get(target, key, receiver)

去获取对应key 的值。

然后是

isSymbol(key)
        ? builtInSymbols.has(key)
        : key === `__proto__` || key === `__v_isRef`

这个判断是当 key 是 Symbol 的某个自有属性,或者是获取 __proto(原型) 和 __v_isRef(Ref 数据,获取这个属性可能是在判断输入的数据是否是 Ref 数据) 对应的值。

接下来的这段代码

if (!isReadonly) { 
  track(target, TrackOpTypes.GET, key)
}

if (shallow) {
  return res
}
if (isRef(res)) {  
  return targetIsArray ? res : res.value
}

if (isObject(res)) { 
  return isReadonly ? readonly(res) : reactive(res)
}

上面的代码相对来说很常规,当获取的是非 readOnly 的数据时进行追踪,然后根据数据的类型去返回对应的响应式的值。

菜鸡小结

总体来说,整个 get 的过程就是针对行的处理了几个特殊属性,漏下来的属性就按照常规属性处理,直接返回判断结果。