Vue3阅读源码系列(四):响应式原理(reactive、ref) 您所在的位置:网站首页 响应式的原理 Vue3阅读源码系列(四):响应式原理(reactive、ref)

Vue3阅读源码系列(四):响应式原理(reactive、ref)

2023-03-28 13:53| 来源: 网络整理| 查看: 265

上一章我们知道了组件是如何挂载渲染的,但是留了一个问题:响应式数据是如何收集的effect对象? 这章我们就从reactive和ref两个声明响应式数据的API入手:

reactive // packages/reactivity/src/reactive.ts export function reactive(target: object) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target } return createReactiveObject( target, false, mutableHandlers, // 普通对象的处理器 mutableCollectionHandlers, // 集合对象的处理器 reactiveMap ) } 复制代码

实际调用的是createReactiveObject

createReactiveObject // 创建响应式对象 function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler, collectionHandlers: ProxyHandler, proxyMap: WeakMap ) { if (!isObject(target)) { // 判断是否是object类型 if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } // target is already a Proxy, return it. // exception: calling readonly() on a reactive object // 如果已经被代理过 直接返回 if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target } // target already has corresponding Proxy const existingProxy = proxyMap.get(target) // 如果已经存在代理对象,直接返回 if (existingProxy) { return existingProxy } // only specific value types can be observed. const targetType = getTargetType(target) // 只有特定的值类型才能被观察 不符合的直接返回 if (targetType === TargetType.INVALID) { return target } // 创建代理对象 const proxy = new Proxy( target, // 类型为集合的使用集合处理器,否则使用基础处理器 (集合类型:Map Set WeakMap WeakSet) targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy } 复制代码

这里使用Proxy代理target,handler根据传入类型判断,我们一般使用baseHandlers,这里其实是传入的mutableHandlers。PS:不熟悉Proxy API的请先去熟悉哦

export const mutableHandlers: ProxyHandler = { get, set, deleteProperty, has, // 处理 in 操作符 ownKeys // 处理 Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()、for...in } 复制代码

我们主要看get和set handler,这是我们最常用的获取和设置值时触发的代理执行,他们分别由createGetter和createSetter创建

createGetter // packages/reactivity/src/baseHandlers.ts // 创建get handler function createGetter(isReadonly = false, shallow = false) { return function get(target: Target, key: string | symbol, receiver: object) { // 一些边界处理 ... // target是否是数组 const targetIsArray = isArray(target) if (!isReadonly) { if (targetIsArray && hasOwn(arrayInstrumentations, key)) { // 数组的特殊处理 arrayInstrumentations为重写的数组方法对象 return Reflect.get(arrayInstrumentations, key, receiver) } if (key === 'hasOwnProperty') { return hasOwnProperty } } // 返回值处理 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 } if (isRef(res)) { // ref unwrapping - skip unwrap for Array + integer key. return targetIsArray && isIntegerKey(key) ? res : res.value } if (isObject(res)) { // 返回值是对象的情况下,执行reactive(递归操作) return isReadonly ? readonly(res) : reactive(res) } return res } } 复制代码

这里的核心是track函数的执行,他会进行收集effect操作,我们来看具体的实现:

track // packages/reactivity/src/effect.ts export function track(target: object, type: TrackOpTypes, key: unknown) { if (shouldTrack && activeEffect) { // targetMap是一个WeakMap实例 let depsMap = targetMap.get(target) if (!depsMap) { // 没有depsMap则设置target为targetMap的键 值为一个Map targetMap.set(target, (depsMap = new Map())) } // 获取dep let dep = depsMap.get(key) if (!dep) { // 没有dep则设置key为depsMap的键 值为一个Set集合 这里放的就是key收集的effect集合 depsMap.set(key, (dep = createDep())) } const eventInfo = __DEV__ ? { effect: activeEffect, target, type, key } : undefined // 收集effect到dep中 trackEffects(dep, eventInfo) } } 复制代码 trackEffects export function trackEffects( dep: Dep, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { let shouldTrack = false if (effectTrackDepth { if (key === 'length' || key >= newLength) { deps.push(dep) } }) } else { // schedule runs for SET | ADD | DELETE if (key !== void 0) { // set add delete操作 将key对应的effect函数添加到deps数组中 deps.push(depsMap.get(key)) } // also run for iteration key on ADD | DELETE | Map.SET // 根据不同的操作push对应的dep switch (type) { case TriggerOpTypes.ADD: if (!isArray(target)) { // 将循环操作的effect函数添加到deps数组中 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 } } const eventInfo = __DEV__ ? { target, type, key, newValue, oldValue, oldTarget } : undefined // 执行triggerEffects 执行dep里所有的effect if (deps.length === 1) { if (deps[0]) { if (__DEV__) { triggerEffects(deps[0], eventInfo) } else { triggerEffects(deps[0]) } } } else { const effects: ReactiveEffect[] = [] for (const dep of deps) { if (dep) { effects.push(...dep) } } if (__DEV__) { triggerEffects(createDep(effects), eventInfo) } else { triggerEffects(createDep(effects)) } } } 复制代码 triggerEffects export function triggerEffects( dep: Dep | ReactiveEffect[], debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] // computed的effect会先执行 // 防止render获取computed值得时候_dirty还没有置为true for (const effect of effects) { if (effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } } } // 执行effect function triggerEffect( effect: ReactiveEffect, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { if (effect !== activeEffect || effect.allowRecurse) { if (__DEV__ && effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } // 如果effect 有调度器 执行调度器 否则执行run 最终都会执行函数更新函数来进行更新 if (effect.scheduler) { effect.scheduler() } else { effect.run() } } } 复制代码

组件的调度器使用了queueJob,他使用异步的方式来优化在一次同步更新中响应式数据多次改变触发多次函数更新函数执行的性能,使得函数更新函数只执行一次。这里不具体展开,有兴趣的可以看具体源码实现,其并不复杂。

ref

ref针对的是单个值的响应式处理,其更加简单,而且没有reactive响应式丢失的问题,我们看它的具体实现:

// packages/reactivity/src/ref.ts export function ref(value?: unknown) { // 创建ref return createRef(value, false) } function createRef(rawValue: unknown, shallow: boolean) { if (isRef(rawValue)) { return rawValue } // 实例化RefImpl类并返回 return new RefImpl(rawValue, shallow) } class RefImpl { private _value: T private _rawValue: T // 依赖集合 public dep?: Dep = undefined public readonly __v_isRef = true // 构造函数 constructor(value: T, public readonly __v_isShallow: boolean) { this._rawValue = __v_isShallow ? value : toRaw(value) // value也可以是复杂数据类型 会执行reactive API将其变成响应式 this._value = __v_isShallow ? value : toReactive(value) } get value() { // 收集依赖 trackRefValue(this) return this._value } set value(newVal) { const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal) newVal = useDirectValue ? newVal : toRaw(newVal) if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) // 触发依赖 triggerRefValue(this, newVal) } } } 复制代码

可以看到ref返回的是一个RefImpl实例,它使用get set存取器,在get中收集依赖,在set中触发依赖,与reactive不同的是ref的依赖集合保存在自身的dep属性,而不是全局的targetMap对象。接下来看看具体的trackRefValue和triggerRefValue操作:

trackRefValue // packages/reactivity/src/ref.ts export function trackRefValue(ref: RefBase) { if (shouldTrack && activeEffect) { ref = toRaw(ref) if (__DEV__) { // 调用trackEffects trackEffects(ref.dep || (ref.dep = createDep()), { target: ref, type: TrackOpTypes.GET, key: 'value' }) } else { trackEffects(ref.dep || (ref.dep = createDep())) } } } 复制代码

最终调用trackEffects,这个函数我们在reactive的收集过程中也调用过,他们是公用。

triggerRefValue export function triggerRefValue(ref: RefBase, newVal?: any) { ref = toRaw(ref) const dep = ref.dep if (dep) { if (__DEV__) { // 触发effect triggerEffects(dep, { target: ref, type: TriggerOpTypes.SET, key: 'value', newValue: newVal }) } else { triggerEffects(dep) } } } 复制代码

这里也是复用triggerEffects去执行收集的依赖



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有