如题,本文是我在阅读 reactive 源码实现过程的记录:
创建响应式对象:
function reactive(target: object) { // 如果原始对象只读 if (isReadonly(target)) { return target } // 创建响应式对象 return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap )}function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any>) { // 非对象 if (!isObject(target)) { return target } // target 已经有对应的响应式对象代理,直接返回代理对象 const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } const proxy = new Proxy( target, // 用 reactive 创建的响应式对象 targetType === TargetType.COLLECTION 的结果是 false targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) // 存储代理对象 proxyMap.set(target, proxy) // 返回代理对象 return proxy}
从上面代码可以看出 reactive 是通过调用 createReactiveObject 函数来创建响应式对象的,它的执行过程如下:
判断 target 是不是一个对象,如果不是直接返回 target 。
判断 proxyMap 中是否已经存在 target 对象的代理对象,如果有直接返回 target 对象。
通过 new Proxy 对 target 配置代理,返回代理对象并赋值给 变量proxy。
将代理对象 proxy 存入 proxyMap。
返回 proxy 对象, 我们在业务代码实际访问到的就是它。
proxyMap :它是一个 WeakMap ,在 reactive 创建响应式对象的过程中起到了性能优化的作用。通过 proxyMap.get(target) 获取已经创建好的 proxy,如果有的话,就不需要再次创建了。WeakMap 持有的是每个键对象的“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行 WeakMap说明
追踪依赖项
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any>) { const proxy = new Proxy( target, // 用 reactive 创建的响应式对象 targetType === TargetType.COLLECTION 的结果是 false targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) // 返回代理对象 return proxy}
上面这段代码只保留了 createReactiveObject 中将 target 包装成响应式对象的代码,响应式的实现都包含在Proxy的 handler 参数里,这儿对应的就是 baseHandlers 。
const get = /*#__PURE__*/ createGetter()// basehandlersconst mutableHandlers: ProxyHandler<object> = { // get 操作,用来获取 reactive 对象上的值, 需要跟踪的依赖项包含在这个函数里面。 get, // set 操作,用来更新 reactive 对象上的值,触发更新包含在这个函数里面。 set, deleteProperty, has, ownKeys}
从上面代码能够看出来,basehandlers.get 就是 createGetter 函数的返回值,下面开始分析这个函数的实现
function createGetter(isReadonly = false, shallow = false) { return function get(target: Target, key: string | symbol, receiver: object) { const targetIsArray = isArray(target) if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) { return Reflect.get(arrayInstrumentations, key, receiver) } // 获取实际的值 const res = Reflect.get(target, key, receiver) // 不是只读属性 if (!isReadonly) { // 在这个函数里面收集依赖 track(target, TrackOpTypes.GET, key) } // res 是一个 ref if (isRef(res)) { // ref unwrapping - does not apply for Array + integer key. const shouldUnwrap = !targetIsArray || !isIntegerKey(key) return shouldUnwrap ? res.value : res } // res 是一个对象 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. // 不是只读的话,将 res 也包装成一个响应式对象 return isReadonly ? readonly(res) : reactive(res) } return res }}
收集依赖的入口就在 track 函数,下面分析这个函数的具体实现:
function track(target: object, type: TrackOpTypes, key: unknown) { // 默认这个判断通过 if (shouldTrack && activeEffect) { let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = createDep())) } const eventInfo = __DEV__ ? { effect: activeEffect, target, type, key } : undefined trackEffects(dep, eventInfo) }}
变量介绍:
targetMap:一个全局变量,用来存储 target 对应的依赖的集合,后续触发更新也会用到它。
depsMap: target对应的依赖集合,里面包含了与key对应的 dep 集合。
dep:用来收集依赖的集合 。
targetMap 的结构图示: track函数的执行过程也是比较清晰的:
通过 targetMap.get(target) 获取 target 对应的依赖集合,并赋值给 depsMap。
如果 depsMap 不存在,执行 targetMap.set(target, (depsMap = new Map())),构造一个空的map对象赋值给 depsMap 变量,存储到 targetMap 的 target 键上。
执行 depsMap.get(key) 获取 depsMap 上 key 对应 Dep 对象,如果不存在,调用 createDep 构造一个 Dep 对象,赋值给 dep 变量,存储到 depsMap 的 key 键上。
调用 trackEffects 追踪依赖项。
不管是 reactive 还是 ref 它们的依赖收集都是在 trackEffects 中完成的
export function trackEffects( dep: Dep, debuggerEventExtraInfo?: DebuggerEventExtraInfo) { let shouldTrack = false if (effectTrackDepth <= maxMarkerBits) { if (!newTracked(dep)) { dep.n |= trackOpBit // set newly tracked shouldTrack = !wasTracked(dep) } } else { // Full cleanup mode. shouldTrack = !dep.has(activeEffect!) } if (shouldTrack) { dep.add(activeEffect!) activeEffect!.deps.push(dep) }}
通知依赖项更新
我们对响应式对象的属性重新赋值的时候会触发页面更新、computed 或者 watch 重新计算或者执行,这里的核心其实就是通知依赖项更新的过程。通知更新的入口就在 createSetter 的返回函数中:
const set = /*#__PURE__*/ createSetter()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 && !isReadonly(value)) { if (!isShallow(value)) { value = toRaw(value) oldValue = toRaw(oldValue) } if (!isArray(target) && isRef(oldValue) && !isRef(value)) { oldValue.value = value return true } } else { // in shallow mode, objects are set as-is regardless of reactive or not } // 是否拥有传入的 key,只判断 target 本身,忽略原型 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 }}
通过 hadkey 判断 target 是否拥有传入的属性,这里会忽略掉 target 原型上的属性,如果是操作原型则不会后续触发更新流程。通知依赖更新的入口函数就是 tigger 函数
export function trigger( target: object, type: TriggerOpTypes, key?: unknown, newValue?: unknown, oldValue?: unknown, oldTarget?: Map<unknown, unknown> | Set<unknown>) { // target 对应的依赖集合 const depsMap = targetMap.get(target) if (!depsMap) { // never been tracked return } let deps: (Dep | undefined)[] = [] // 是数组 if (key === 'length' && isArray(target)) { depsMap.forEach((dep, key) => { if (key === 'length' || key >= (newValue as number)) { deps.push(dep) } }) } else { // schedule runs for SET | ADD | DELETE if (key !== void 0) { // 将 despMap 中 key 对应的 dep 集合取出 deps.push(depsMap.get(key)) } // 添加 iterate 到 deps 中 switch (type) { case TriggerOpTypes.ADD: if (!isArray(target)) { deps.push(depsMap.get(ITERATE_KEY)) if (isMap(target)) { deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)) } } else if (isIntegerKey(key)) { // new index added to array -> length changes deps.push(depsMap.get('length')) } break case TriggerOpTypes.DELETE: if (!isArray(target)) { deps.push(depsMap.get(ITERATE_KEY)) if (isMap(target)) { deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)) } } break case TriggerOpTypes.SET: if (isMap(target)) { deps.push(depsMap.get(ITERATE_KEY)) } break } } // 通知 deps 收集到的所有依赖项更新 if (deps.length === 1) { if (deps[0]) { triggerEffects(deps[0]) } } else { // 正常情况下不会进入else,省略这部分代码 } }export function triggerEffects( dep: Dep | ReactiveEffect[], debuggerEventExtraInfo?: DebuggerEventExtraInfo) { // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { if (effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } }}function triggerEffect( effect: ReactiveEffect, debuggerEventExtraInfo?: DebuggerEventExtraInfo) { if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } if (effect.scheduler) { effect.scheduler() } else { effect.run() } }}
trigger 函数的逻辑很清晰:
targetMap.get(target) 获取 target 对应的 dep 集合。
执行 deps.push(depsMap.get(key)) 将 key 对应的 dep 集合 push 到 deps 数组。
调用 triggerEffects 遍历 dep 集合通知对应依赖更新。
原文:https://juejin.cn/post/7095640961115488269