首页>>前端>>Vue->vue3源码阅读

vue3源码阅读

时间:2023-11-30 本站 点击:1

如题,本文是我在阅读 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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/3731.html