关于 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。其实这里只需要直到,一般情况下 proxy 的 handler 的 get 函数的参数 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 的过程就是针对行的处理了几个特殊属性,漏下来的属性就按照常规属性处理,直接返回判断结果。