zhangzheng-zz / blog

1 stars 0 forks source link

Vue3源码全梳理 #16

Open zhangzheng-zz opened 3 years ago

zhangzheng-zz commented 3 years ago

参考:https://www.gitsu.cn/detail?id=82

响应式系统:

1615218691549379

Vue3响应式系统依旧通过数据劫持的方式,改用Proxy做代理。取值的时候track,赋值的时候trigger,effect将正在调用这个对象的函数activeEffect和响应式对象key联系起来实现响应式系统。

reactive

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  // ...
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  return proxy
}

get && set

function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {

    const res = Reflect.get(target, key, receiver)

    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }
    return res
  }
}

function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    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
  }
}

track&trigger

export function track(target: object, type: TrackOpTypes, key: unknown) {
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  //activeEffect 正在调用这个对象的函数
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
  }
}

export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  const depsMap = targetMap.get(target)
  const effects = new Set<ReactiveEffect>()
    // 有 scheduler 就先入队 queue
    if (effect.options.scheduler) {
      effect.options.scheduler(effect)
    } else {
      effect()
    }
  }

  effects.forEach(run)
}

三个存储变量:targetMap (WeakMap) 对象 -> depsMap (Map) 依赖 -> dep(Set) 副作用

对于 Map WeakMap Set WeakSet 的各种api操作的劫持怎么实现?

我们使用 get、set、has等这些 api 的时候会触发 get,拿到的是这些操作函数,我们使用函数劫持,只要劫持一个 get方法(即对对象的key的访问)并替换成自己封装的方法。

export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
  get: createInstrumentationGetter(false, false)
}

const mutableInstrumentations: Record<string, Function> = {
  get(this: MapTypes, key: unknown) {
    return get(this, key)
  },
  get size() {
    return size((this as unknown) as IterableCollections)
  },
  has,
  add,
  set,
  delete: deleteEntry,
  clear,
  forEach: createForEach(false, false)
}

Vue2 vs Vue3

Vue3 使用 Proxy:

// new index added to array -> length changes add(depsMap.get('length'))