vue3源码学习5-响应式

在vue3中,使用reactive API把一个对象数据变成响应式,看看怎么实现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
  )
}

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {
	// 目标必须是对象或者数组类型
    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
  // target已经是proxy对象,直接返回
  // 但是,target如果是readonly作用于一个响应式对象,继续
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  // target已经是proxy对象,返回已有的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
  }
  // 利用Proxy创建响应式
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  // 给原始数据打个标识,说明他已经变成响应式,且有对应的Proxy
  proxyMap.set(target, proxy)
  return proxy
}

接下来看Proxy处理器对象mutableHandlers的实现packages/reactivity/src/baseHandlers.ts

// 主要就是劫持对observe对象的操作:
// 访问对象属性会触发get函数
// 设置对象属性会触发set函数
// 删除对象属性会触发deleteProperty函数
// in操作符会触发has函数
// 通过Object.getOwnPropertyNames访问对象属性名会触发ownKeys函数
export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}

无论命中哪个函数,都会做依赖收集或者派发通知,所以只用看一下set和get函数:

//依赖收集发生在数据访问阶段,当我们用Proxy API劫持数据后,当这个响应式对象属性被访问时,就会执行get函数
// get就是执行了createGetter()的返回值
const get = /*#__PURE__*/ createGetter()
// 来看createGetter()实现
function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {
    if (key === ReactiveFlags.IS_REACTIVE) {
	  // 代理了observed.__v_isReactive
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
	  // 代理了observed.__v_isReadOnly
      return isReadonly
    } else if (key === ReactiveFlags.IS_SHALLOW) {
	  // 代理了observed.__v_shallow
      return shallow
    } else if (
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
          ? shallowReactiveMap
          : reactiveMap
        ).get(target)
    ) {
	  // 代理了observed.__v_raw
      return target
    }

    const targetIsArray = isArray(target)
	// arrayInstrumentations包含对数组一些方法修改的函数
    if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
      return Reflect.get(arrayInstrumentations, key, receiver)
    }
	// 求值
    const res = Reflect.get(target, key, receiver)
	// 内置的 Symbol key不需要依赖收集
    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)) {
      // 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是个对象或数组类型,则递归执行reactive函数把res变成响应式
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}

来看arrayInstrumentations对数组的修改packages/reactivity/src/baseHandlers.ts:

// 也就是执行createArrayInstrumentations()的返回值
const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations()
function createArrayInstrumentations() {
  const instrumentations: Record<string, Function> = {}
  // instrument identity-sensitive Array methods to account for possible reactive
  // values
  ;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
    instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
	  // toRaw可以把响应式对象转为原始对象
      const arr = toRaw(this) as any
      for (let i = 0, l = this.length; i < l; i++) {
		// 依赖收集
        track(arr, TrackOpTypes.GET, i + '')
      }
      // we run the method using the original args first (which may be reactive)
	  // 先尝试用参数本身,可能是响应式数据
      const res = arr[key](...args)
      if (res === -1 || res === false) {
        // if that didn't work, run it again using raw values.
		// 如果失败,再尝试把参数转为原始数据
        return arr[key](...args.map(toRaw))
      } else {
        return res
      }
    }
  })
  // instrument length-altering mutation methods to avoid length being tracked
  // which leads to infinite loops in some cases (#2137)
  ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
    instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
      pauseTracking()
      const res = (toRaw(this) as any)[key].apply(this, args)
      resetTracking()
      return res
    }
  })
  return instrumentations
}

整个get函数的核心就是执行track依赖收集packages/reactivity/src/effect.ts:

// 原始对象Map
const targetMap = new WeakMap<any, KeyToDepMap>()
// 当前激活的effect
export let activeEffect: ReactiveEffect | undefined
// 是否应该收集依赖
export let shouldTrack = true

export function track(target: object, type: TrackOpTypes, key: unknown) {
  if (shouldTrack && activeEffect) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
	  // 每个key对应一个dep集合
      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)
  }
}
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) {
	// 收集当前激活的effect作为依赖
    dep.add(activeEffect!)
	// 当前激活的effect收集dep集合作为依赖
    activeEffect!.deps.push(dep)
    if (__DEV__ && activeEffect!.onTrack) {
      activeEffect!.onTrack({
        effect: activeEffect!,
        ...debuggerEventExtraInfo!
      })
    }
  }
}

派发通知set函数,因为我们用Proxy劫持了数据对象,所以响应式对象属性更新的时候就会执行set函数packages/reactivity/src/baseHandlers.ts:

// 执行了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) {
      if (!isShallow(value) && !isReadonly(value)) {
        oldValue = toRaw(oldValue)
        value = toRaw(value)
      }
      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
	// 如果target的原型链也是一个proxy, 通过Reflect.set修改原型链上的属性会再次触发setter,这种情况下就没有必要触发两次trigger了
    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
  }
}

set函数的核心是执行trigger函数packages/reactivity/src/effect.ts:

// 原始数据对象Map
const targetMap = new WeakMap<any, KeyToDepMap>()
export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  // 通过targetMap拿到target对应的依赖合集
  const depsMap = targetMap.get(target)
  if (!depsMap) {
	// 如果没有依赖,直接返回
    // never been tracked
    return
  }
	// SET | ADD | DELETE之一的操作,添加对应的dep
	if (key !== void 0) {
      deps.push(depsMap.get(key))
    }

    // also run for iteration key on ADD | DELETE | Map.SET
    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
    }

  // 创建运行的effects集合,并全部添加到effects
  const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep)
      }
    }
	triggerEffects(createDep(effects))
}

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()
    }
  }
}

之前一直说到的副作用函数packages/reactivity/src/effect.ts:

// 
export function effect<T = any>(
  fn: () => T,
  options?: ReactiveEffectOptions
): ReactiveEffectRunner {
  if ((fn as ReactiveEffectRunner).effect) {
	// 如果fn已经是effect函数,则指向原函数
    fn = (fn as ReactiveEffectRunner).effect.fn
  }
  // 创建一个_effect,它是一个响应式的副作用函数
  const _effect = new ReactiveEffect(fn)
  if (options) {
    extend(_effect, options)
    if (options.scope) recordEffectScope(_effect, options.scope)
  }
  if (!options || !options.lazy) {
	// lazy配置,计算属性会用到,非lazy则直接执行一次
    _effect.run()
  }
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  runner.effect = _effect
  return runner
}

// 执行的_effect.run,也就是ReactiveEffect的run方法
export class ReactiveEffect<T = any> {
  run() {
    if (!this.active) {
	  // 非激活状态,直接执行原始函数
      return this.fn()
    }
    let parent: ReactiveEffect | undefined = activeEffect
    let lastShouldTrack = shouldTrack
    while (parent) {
      if (parent === this) {
        return
      }
      parent = parent.parent
    }
    try {
      this.parent = activeEffect
      activeEffect = this
	  // 开启全局shouldTrack, 允许依赖收集
      shouldTrack = true
	  // 压栈
      trackOpBit = 1 << ++effectTrackDepth

      if (effectTrackDepth <= maxMarkerBits) {
        initDepMarkers(this)
      } else {
        cleanupEffect(this)
      }
	  // 执行原始函数
      return this.fn()
    } finally {
      if (effectTrackDepth <= maxMarkerBits) {
	    // 指向栈最后一个effect
        finalizeDepMarkers(this)
      }
	  // 出栈
      trackOpBit = 1 << --effectTrackDepth
	  // 恢复shouldTrack开启之前的状态
      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined

      if (this.deferStop) {
        this.stop()
      }
    }
  }
}

前面发现reactive API只能接收对象或数组类型,不支持基础数据类型。所以vue3设计了ref API:

packages/reactivity/src/ref.ts

export function ref(value?: unknown) {
  return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
  // 如果传入的就是一个ref,直接返回就行,处理嵌套ref
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
  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)
	// 如果是对象或者数组,转为一个reactive对象
    this._value = __v_isShallow ? value : toReactive(value)
  }

  get value() {
	// 依赖收集,key为固定 的value
    trackRefValue(this)
    return this._value
  }

  set value(newVal) {
    const useDirectValue =
      this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
    newVal = useDirectValue ? newVal : toRaw(newVal)
	// 只处理value属性的修改
    if (hasChanged(newVal, this._rawValue)) {
	  // 判断有变化后,更新值
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)
	  // 派发通知
      triggerRefValue(this, newVal)
    }
  }
}
posted @ 2022-09-06 16:24  菜菜123521  阅读(61)  评论(0编辑  收藏  举报