baseHandler
reactivity/src/baseHandlers.ts
getter
创建getter函数, 用在proxy的handlers中, 获取到的值中, 如果是非shadowReactive且非基本类型的, 此时转成reactive后返回, 让它可以继续触发响应.
/** 创建getter函数, */
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
// 获取flag值
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
return shallow
} else if (
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}
const targetIsArray = isArray(target)
// 获取array的原有属性, indexOf, includes等
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
const res = Reflect.get(target, key, receiver)
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
// 如果不是只读, 此时触发收集
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 浅层直接返回
if (shallow) {
return res
}
// 非浅层, 自动解包ref, 这里不需要再转一次reactive是因为如果value是object的情况下, 默认会转成reactive
if (isRef(res)) {
return targetIsArray && isIntegerKey(key) ? res : res.value
}
// 如果不是readonly, 则此时用reactive包装.
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
setter
创建setter函数, 用于proxy的handlers中, 先缓存旧值, 然后判断旧值是否只读等, 如果否, 再判断旧值是否等于新值, 如果不相等, 则触发回调.
/** 创建setter函数 */
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
let oldValue = (target as any)[key]
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false
}
if (!shallow) {
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
value = toRaw(value)
}
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
// 如果oldValue是ref, 不在此处触发回调.
oldValue.value = value
return true
}
} else {
// in shallow mode, objects are set as-is regardless of reactive or not
}
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
if (target === toRaw(receiver)) {
// 触发回调
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
delete, has, ownKeys
其他的触发函数
/** 拦截删除操作, 触发watch */
function deleteProperty(target: object, key: string | symbol): boolean {
const hadKey = hasOwn(target, key)
const oldValue = (target as any)[key]
const result = Reflect.deleteProperty(target, key)
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}
/** 拦截has操作, 触发依赖收集 */
function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key)
if (!isSymbol(key) || !builtInSymbols.has(key)) {
track(target, TrackOpTypes.HAS, key)
}
return result
}
function ownKeys(target: object): (string | symbol)[] {
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
return Reflect.ownKeys(target)
}
总结
- 使用
ref,computed,reactive时, 会分别用class的getter和setter函数, 或者使用proxy的方式进行劫持, 如果变量是基础类型, 则使用ref的方式, 如果变量是对象类型, 则使用proxy的方式进行, 在proxy的方式中, 获取到的新值如果也是对象, 则此时也会转为proxy的方式返回. - 在获取变量值时, 会调用
track函数进行依赖收集, 此时收集到的依赖保存为targetMap[target][key]中, 另外同时保存在activeEffect!.deps中 - 在触发setter函数后, 调用
trigger函数触发重新计算computed或effect, 此时从targetMap[target][key]中取出对应的deps列表, 并且分不同类型进行触发操作, 最后调用的是effect.scheduler()或者effect.run().