Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue3 reactive源码解析 #9

Open
MyPrototypeWhat opened this issue Dec 14, 2021 · 0 comments
Open

Vue3 reactive源码解析 #9

MyPrototypeWhat opened this issue Dec 14, 2021 · 0 comments

Comments

@MyPrototypeWhat
Copy link
Owner

vue-reactive

  • 入口

    • path:packages/reactivity/src/reactive.ts

      export const enum ReactiveFlags {
      // 初始都为undefined
      SKIP = '__v_skip', //无需响应的对象
      IS_REACTIVE = '__v_isReactive', //响应式对象
      IS_READONLY = '__v_isReadonly', //只读数据
      RAW = '__v_raw' //取原始对象
      }
      const enum TargetType {
      INVALID = 0, // 无效
      COMMON = 1, // Array/Object
      COLLECTION = 2 // map/set/weakMap/weakSet
      }
      export function reactive(target: object) {
        // 如果尝试观察只读代理,请返回只读版本。
        if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
          return target
        }
        return createReactiveObject(
          target,
          false,
          mutableHandlers,
          mutableCollectionHandlers,
          reactiveMap
        )
      }
    • createReactiveObject

      // 机翻下英文注释就能看明白...
      function createReactiveObject(
        target: Target,
        isReadonly: boolean,// reactive函数调用传参为false
        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
        }
        // 目标已是代理,请返回它。
        // 此处调用会触发getter
        if (
          target[ReactiveFlags.RAW] &&
          !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
        ) {
          return target
        }
        // 目标已具有相应的代理
        // 每个 proxy 都会被保存在 proxyMap
        const existingProxy = proxyMap.get(target)
        if (existingProxy) {
          return existingProxy
        }
      
        function targetTypeMap(rawType: string) {
          switch (rawType) {
            case 'Object':
            case 'Array':
              return TargetType.COMMON
            case 'Map':
            case 'Set':
            case 'WeakMap':
            case 'WeakSet':
              return TargetType.COLLECTION
            default:
              return TargetType.INVALID
          }
        }
        function getTargetType(value: Target) {
          return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
            ? TargetType.INVALID // 0
            // toRawType => Object.prototype.toString.call(value).slice(8,-1)
            : targetTypeMap(toRawType(value)) // 1|2
        }
       
        // 0直接返回target
        const targetType = getTargetType(target)
        if (targetType === TargetType.INVALID) {
          return target
        }
        // 添加代理
        const proxy = new Proxy(
          target,
          // 1=>baseHandlers 2->collectionHandlers
          targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
        )
        // target -> proxy
        proxyMap.set(target, proxy)
        return proxy
      }
  • baseHandlers

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

      const get = createGetter()
      function createGetter(isReadonly = false, shallow = false) {
        return function get(target: Target, key: string | symbol, receiver: object) {
          if (key === ReactiveFlags.IS_REACTIVE) {
            return !isReadonly
          } else if (key === ReactiveFlags.IS_READONLY) {
            return isReadonly
          } else if (
            // 如果key==='__v_raw' && receiver===reactiveMap(也就是上文的proxyMap).get(target)
            key === ReactiveFlags.RAW &&
            receiver ===
              (isReadonly
                ? shallow
                  ? shallowReadonlyMap
                  : readonlyMap
                : shallow
                ? shallowReactiveMap
                : reactiveMap
              ).get(target)
          ) {
            // 当target[__v_raw]取值时 返回target
            return target
          }
      	// isArray = Array.is
          const targetIsArray = isArray(target)
      	// hasOwn = Object.prototype.hasOwnProperty.call
          // 判断 target是数组,并且调用了数组的方法
          // arrayInstrumentations是 一个对象{[数组方法]:function(){}}
          if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
            return Reflect.get(arrayInstrumentations, key, receiver)
          }
      	
          const res = Reflect.get(target, key, receiver)
      	// isSymbol = typeof val === 'symbol'
          const builtInSymbols = new Set(
            Object.getOwnPropertyNames(Symbol)
              .map(key => (Symbol as any)[key])
              .filter(isSymbol)
          )
          
          if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
            // 如果key是Symbol,则判断key是否是symbol内置属性
            // 否则判断key是否在__proto__,__v_isRef,__isVue属性上
            return res
          }
      
          if (!isReadonly) {
            // 不是制只读
            track(target, TrackOpTypes.GET, key)
          }
      
          if (shallow) {
            return res
          }
      
          if (isRef(res)) {
            // 判断是否被ref修饰过
            // ref展开-不适用于数组+整数键。
            const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
            return shouldUnwrap ? res.value : res
          }
      
          if (isObject(res)) {
            // 将返回值也转换为代理。
            // 我们在这里执行isObject检查以避免无效值警告。
            // 这里还需要延迟访问只读和被动,以避免循环依赖。
            // 返回proxy,并在上方判断该对象是否被代理,如果被代理直接返回改
            return isReadonly ? readonly(res) : reactive(res)
          }
      
          return res
        }
      }
      • arrayInstrumentations

        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[]) {
              const arr = toRaw(this) as any
              for (let i = 0, l = this.length; i < l; i++) {
                track(arr, TrackOpTypes.GET, i + '')
              }
              // 我们首先使用原始参数运行该方法(可能是被动的)
              const res = arr[key](...args)
              if (res === -1 || res === false) {
                // 如果不起作用,请使用原始值再次运行它。
                return arr[key](...args.map(toRaw))
              } else {
                return res
              }
            }
          })
          //避免无限调用
          ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
            instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
              pauseTracking()
              // 触发getter,获取原始值调用对应函数
              const res = (toRaw(this) as any)[key].apply(this, args)
              resetTracking()
              return res
            }
          })
          return instrumentations
        }
  • effect:

    • path:packages/reactivity/src/effect.ts

    • track:

      let activeEffect // ReactiveEffect实例
      let trackOpBit = 1
      export function track(target: object, type: TrackOpTypes, key: unknown) {
        if (!isTracking()) {
          // 没有依赖
          // 看到此处 ,如果是单纯的get、set 没有使用effect的话,建议跳过下面部分,直接看set
          return
        }
        // targetMap: target -> depsMap
        let depsMap = targetMap.get(target)
        if (!depsMap) {
          targetMap.set(target, (depsMap = new Map()))
        }
        // depsMap: key -> dep
        let dep = depsMap.get(key)
        const createDep = (effects?: ReactiveEffect[]): Dep => {
          // Set 防止重复
          const dep = new Set<ReactiveEffect>(effects) as Dep
          // 用来标记该属性上次和本次在哪些effect中使用过,再通过对比进行删除和新增。
          dep.w = 0
          dep.n = 0
          return dep
        }
        if (!dep) {
          depsMap.set(key, (dep = createDep()))
        }
      
        const eventInfo = __DEV__
          ? // debugger用,不需要关注
            { effect: activeEffect, target, type, key }
          : undefined
      
        trackEffects(dep, eventInfo)
      }
      export function isTracking() {
        return shouldTrack && activeEffect !== undefined
      }
      
      export function trackEffects(
        dep: Dep,
        debuggerEventExtraInfo?: DebuggerEventExtraInfo
      ) {
        let shouldTrack = false
        // effectTrackDepth: 当前递归跟踪的effect数。 用来记录当前effect是第几层,每当有effect执行就++effectTrackDepth,执行完毕就--effectTrackDepth
      
        // maxMarkerBits=30:按位轨迹标记最多支持30级递归。选择此值是为了使现代JS引擎能够在所有平台上使用SMI。当递归深度更大时,返回到使用完全清理。
      
        if (effectTrackDepth <= maxMarkerBits) {
          // 判断本次是否标记过
          if (!newTracked(dep)) {
            // trackOpBit 可以理解为唯一ID
            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)
            
          if (__DEV__ && activeEffect!.onTrack) {
            activeEffect!.onTrack(
              Object.assign(
                {
                  effect: activeEffect!
                },
                debuggerEventExtraInfo
              )
            )
          }
        }
      }
    • effect:

      export function effect<T = any>(
        fn: () => T,
        options?: ReactiveEffectOptions
      ): ReactiveEffectRunner {
        if ((fn as ReactiveEffectRunner).effect) {
          // 拿到原始fn
          fn = (fn as ReactiveEffectRunner).effect.fn
        }
      
        const _effect = new ReactiveEffect(fn)
        if (options) {
          // extend = Object.assign
          extend(_effect, options)
          if (options.scope) recordEffectScope(_effect, options.scope)
        }
        if (!options || !options.lazy) {
      		// 没有lazy参数,立即执行run    
          _effect.run()
        }
        const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
        runner.effect = _effect
        return runner
      }
      • ReactiveEffect

        const effectStack=[]
        class ReactiveEffect {
              constructor(fn, scheduler = null, scope) {
                  this.fn = fn;
                  this.scheduler = scheduler;
                  this.active = true;
                  this.deps = [];
                	// effectScope 相关处理逻辑
                  recordEffectScope(this, scope);
              }
              run() {
                  if (!this.active) {
                    // 没有激活,说明我们调用了effect stop 函数,
                      return this.fn();
                  }
                  if (!effectStack.includes(this)) {
                      try {
                          effectStack.push((activeEffect = this));
                          enableTracking();
                        	// 根据递归的深度记录位数
                          trackOpBit = 1 << ++effectTrackDepth;
                          if (effectTrackDepth <= maxMarkerBits) {
                          // 给依赖打标记
                              initDepMarkers(this);
                          }
                          else {
                           // 超过 maxMarkerBits 则 trackOpBit 的计算会超过最大整形的位数,
                          // 降级为 cleanupEffect
                              cleanupEffect(this);
                          }
                          return this.fn();
                      }
                      finally {
                        	// fn执行完成之后
                          if (effectTrackDepth <= maxMarkerBits) {
                              // 完成依赖标记
                              finalizeDepMarkers(this);
                          }
                        	// 恢复到上一级
                          trackOpBit = 1 << --effectTrackDepth;
                          resetTracking();
                        	// 出栈
                          effectStack.pop();
                          const n = effectStack.length;
                        	// 指向栈最后一个 effect
                          activeEffect = n > 0 ? effectStack[n - 1] : undefined;
                      }
                  }
              }
              stop() {
                  if (this.active) {
                      cleanupEffect(this);
                      if (this.onStop) {
                          this.onStop();
                      }
                      this.active = false;
                  }
              }
          }
        const initDepMarkers = ({ deps }) => {
              if (deps.length) {
                  for (let i = 0; i < deps.length; i++) {
                      deps[i].w |= trackOpBit; // 标记依赖已经被收集
                  }
              }
          };
  • 例子

    • 看到这就能捋一下get
    Vue.createApp({
      setup(){
        const state=reactive({a:1})
        effect(()=>{
          console.log(state.a)
        })
        return {state}
      }
    }).mount('#app')
    • 映射关系
      • proxyMap={target:proxy}targetMap={target:depsMap}depsMap={key:dep}dep={Set[ReactiveEffect实例],n,w}
    • 流程:
      • reactive:创建proxygetter收集依赖setter触发effect
      • effect:创建ReactiveEffect实例,执行run函数,state.a触发getter,触发track,根据target,key找出对应dep,将ReactiveEffect实例添加进depReactiveEffect实例deps数组推入dep
      • 遍历ReactiveEffect.deps,也就是dep,更w,n属性,effectStack出栈,activeEffect指向effectStack栈顶
  • Setter

    function createSetter(shallow = false) {
      return function set(
        target: object,
        key: string | symbol,
        value: unknown,
        receiver: object
      ): boolean {
        let oldValue = (target as any)[key]
        if (!shallow && !isReadonly(value)) {
          // 获取新旧真实数据
          value = toRaw(value)
          oldValue = toRaw(oldValue)
          if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
            // 处理ref数据
            oldValue.value = value
            return true
          }
        } else {
          // 在浅层模式下,无论是否是reactive,对象都按原样设置
        }
    	// 判断有效key
        const hadKey =
          isArray(target) && isIntegerKey(key)
            ? Number(key) < target.length
            : hasOwn(target, key)
        const result = Reflect.set(target, key, value, receiver)
        // 如果目标是原型链中的某个东西,则不要触发
        // receiver是proxy实例对象,原值为Proxy {...target}
        // 此时Reflect.set执行过后,receiver变为Proxy {key:value} 
        // proxyMap中相应的target也更改为{key:value} 
        // toRaw获取__v_raw,会触发getter,返回proxyMap.get(target)
        if (target === toRaw(receiver)) {
          if (!hadKey) {
            trigger(target, TriggerOpTypes.ADD, key, value)
          } else if (hasChanged(value, oldValue)) {
            // 对比新旧值是否改变
            // hasChanged = !Object.is
            trigger(target, TriggerOpTypes.SET, key, value, oldValue)
          }
        }
        return result
      }
    }
    • 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) {
          // 没有被收集
          return
        }
        
        let deps: (Dep | undefined)[] = []
        if (type === TriggerOpTypes.CLEAR) {
          // 正在清除集合
          // 触发target中所有effect
          deps = [...depsMap.values()]
        } else if (key === 'length' && isArray(target)) {
          // 对修改数组length的处理
          depsMap.forEach((dep, key) => {
            if (key === 'length' || key >= (newValue as number)) {
              deps.push(dep)
            }
          })
        } else {
          
          if (key !== void 0) {
            deps.push(depsMap.get(key))
          }
      
          // 在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
          }
        }
      
        const eventInfo = __DEV__
          ? { target, type, key, newValue, oldValue, oldTarget }
          : undefined
      
        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
        for (const effect of isArray(dep) ? dep : [...dep]) {
          // effect = ReactiveEffect{}
          // 遍历依赖执行
          if (effect !== activeEffect || effect.allowRecurse) {
            if (__DEV__ && effect.onTrigger) {
              effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
            }
            if (effect.scheduler) {
              effect.scheduler()
            } else {
              effect.run()
            }
          }
        }
      }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant