〖Vue3 源码笔记04〗响应包裹reactive(data),Vue3响应核心effect

92 阅读2分钟

v3.0.0-beta.1 2020-4-17

reactive

// packages\reactivity\src\reactive.ts
const rawToReactive = new WeakMap<any, any>()
const reactiveToRaw = new WeakMap<any, any>()

export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (readonlyToRaw.has(target)) {
    // 缓存
    return target
  }
  return createReactiveObject(
    target,
    rawToReactive,
    reactiveToRaw,
    mutableHandlers,
    mutableCollectionHandlers
  )
}

createReactiveObject

// packages\reactivity\src\reactive.ts
const rawToReactive = new WeakMap<any, any>()
const reactiveToRaw = new WeakMap<any, any>()

function createReactiveObject(
  target: unknown,
  toProxy: WeakMap<any, any>,
  toRaw: WeakMap<any, any>,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  if (!isObject(target)) return target
  
  // 双缓存
  let observed = toProxy.get(target)
  if (observed !== void 0) return observed
  if (toRaw.has(target)) return target
  
  // 响应黑名单
  if (!canObserve(target)) return target
  
  const handlers = collectionTypes.has(target.constructor)
    ? collectionHandlers
    : baseHandlers
  observed = new Proxy(target, handlers)
  
  // 双缓存
  toProxy.set(target, observed)
  toRaw.set(observed, target)
  return observed
}

mutableHandlers

// packages\reactivity\src\baseHandlers.ts
export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}

const get = /*#__PURE__*/ createGetter()
const set = /*#__PURE__*/ createSetter()

function createGetter(isReadonly = false, shallow = false) {
  return function get(target: object, key: string | symbol, receiver: object) {
    // 数组Array
    const targetIsArray = isArray(target)
    if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
      return Reflect.get(arrayInstrumentations, key, receiver)
    }
    
    const res = Reflect.get(target, key, receiver)
    // Symbol出口
    if (isSymbol(key) && builtInSymbols.has(key)) {
      return res
    }
    // shallow出口
    if (shallow) {
      !isReadonly && track(target, TrackOpTypes.GET, key)
      return res
    }
    // ref出口
    if (isRef(res)) {
      if (targetIsArray) {
        !isReadonly && track(target, TrackOpTypes.GET, key)
        return res
      } else {
        // ref unwrapping, only for Objects, not for Arrays.
        return res.value
      }
    }
    
    // 进入Effect
    !isReadonly && track(target, TrackOpTypes.GET, key)
    
    // 递归返回
    return isObject(res)
      ? isReadonly
        ? // need to lazy access readonly and reactive here to avoid
          // circular dependency
          readonly(res)
        : reactive(res)
      : res
    }
}

function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    const oldValue = (target as any)[key]
    
    value = toRaw(value)
      if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
        // ref出口口
        oldValue.value = value
        return true
      }
    
    const hadKey = hasOwn(target, key)
    const result = Reflect.set(target, key, value, receiver)
    
    // 触发依赖
    if (hasChanged(value, oldValue)) {
     trigger(target, TriggerOpTypes.SET, key, value, oldValue)
    }
    
    return result
  }
}

track依赖

// packages\reactivity\src\effect.ts
const targetMap = new WeakMap<any, KeyToDepMap>()

export function track(target: object, type: TrackOpTypes, key: unknown) {

  // 依赖Map
  let depsMap = targetMap.get(target)
  if (depsMap === void 0) {
    targetMap.set(target, (depsMap = new Map()))
  }
  
  // key Map,key依赖Set
  let dep = depsMap.get(key)
  if (dep === void 0) {
    depsMap.set(key, (dep = new Set()))
  }
  
  // ?vue2 的 render Observer?
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
  }
}

trigger触发

export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  // 取出依赖
  const depsMap = targetMap.get(target)
  if (depsMap === void 0) {
    return
  }
  
  // 添加
  const effects = new Set<ReactiveEffect>()
  const computedRunners = new Set<ReactiveEffect>()
  const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
    if (effectsToAdd !== void 0) {
      effectsToAdd.forEach(effect => {
        if (effect !== activeEffect || !shouldTrack) {
          if (effect.options.computed) {
            computedRunners.add(effect)
          } else {
            effects.add(effect)
          }
        }
      })
    }
  }
  add(depsMap.get(key))
  
  // 运行
  const run = (effect: ReactiveEffect) => {
    if (effect.options.scheduler !== void 0) {
      effect.options.scheduler(effect)
    } else {
      effect()
    }
  }
  computedRunners.forEach(run)
  effects.forEach(run)
}

小结

get 收集 activeEffect,set 运行 effects.forEach(run) => effect()