来个漫画?

png

watchEffect例子

镜.png

响应式

先看以下与响应式相关常用API有哪些。

// effect
export declare function effect<T = any>(fn: () => T, options?: ReactiveEffectOptions): ReactiveEffect<T>;

// 是不是响应式数据
export declare function isReactive(value: unknown): boolean;

// 是不是只读属性
export declare function isReadonly(value: unknown): boolean;

// 是不是Ref
export declare function isRef<T>(r: Ref<T> | unknown): r is Ref<T>;

// 标记,表示不进行响应式数据化
export declare function markRaw<T extends object>(value: T): T;

// 对象类型的响应式数据
export declare function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;

// 基本数据类型的响应式数据
export declare function ref<T>(value: T): Ref<UnwrapRef<T>>;

测试用例

const a = reactive({ foo: 1 })
const rf = ref('hello')
const ef = effect(() => {
  console.log(a.foo) // 因为a是Proxy类型,可以追踪getter的动作。
  console.log(rf.value)  // 同上
})

reactive:

传入的是对象,但你传基本数据会直接不对target进行处理,直接返回。

export function reactive(target: object) { 
  // { msg: 1, __v_isReadonly: true } 这样就会直接返回target什么都不做,ReactiveFlags.isReadonly标记
  if (target && (target as Target)[ReactiveFlags.isReadonly]) {
    return target
  }

  // setup执行
  return createReactiveObject(
    target,
    false, // 设置isReadonly为false
    mutableHandlers, // 设置proxy的hander
    mutableCollectionHandlers // 设置Map Set WeakMap WeackSet类型的hander
  )
}

createReactiveObject:

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  // 检查是不是Object 如果不是obejct 报错 且在dev环境直接不做任何处理 返回target
  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已经是响应式数据,或者是readlyOnly数据
  // 都会直接返回target    
  if (
    target[ReactiveFlags.raw] &&
    !(isReadonly && target[ReactiveFlags.isReactive])
  ) {
    return target
  }

  // target already has corresponding Proxy
  if (
    hasOwn(target, isReadonly ? ReactiveFlags.readonly : ReactiveFlags.reactive)
  ) {
    return isReadonly
      ? target[ReactiveFlags.readonly]
      : target[ReactiveFlags.reactive]
  }

  // 拥有__v_skip 属性的 或者被Object.frozen处理过的都不做任何处理
  if (!canObserve(target)) {
    return target
  }

  const observed = new Proxy(
    target,
    // object不属于Set Map WeakSetWeakMap,选用baseHandlers。
    // 如果属于则选用  collectionHandlers 
    collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
  )

  // target['__v_reactive'] = observed 互相指引
  def(
    // { msg, root } 然后做一个整体观察者proxy
    target,
    // "__v_readonly" /* readonly */ : "__v_reactive" /* reactive */
    isReadonly ? ReactiveFlags.readonly : ReactiveFlags.reactive,
    // proxy
    observed
  )

  return observed
}

createRef,ref等于createRef(target, false)

function createRef(rawValue: unknown, shallow = false) {
  // __v_isRef === true,则直接返回  
  if (isRef(rawValue)) {
    return rawValue
  }

  // 如果传入的是对象,则直接进行reactive(target),否则直接返回rawValue。
  // shallow 一层响应式 shallowRef的时候可以用到,只监听value属性
  let value = shallow ? rawValue : convert(rawValue)

  // 对get和set行为,进行监听
  const r = {
    __v_isRef: true,
    get value() {
      // 追踪
      track(r, TrackOpTypes.GET, 'value')
      return value
    },
    set value(newVal) {
      // toRow 为了避免传入的还是响应式数据结构
      if (hasChanged(toRaw(newVal), rawValue)) {
        // 保存  
        rawValue = newVal

        // 再次判是否需要转换  
        value = shallow ? newVal : convert(newVal)

        // 触发所追踪的effect  
        trigger(
          r,
          TriggerOpTypes.SET,
          'value',
          __DEV__ ? { newValue: newVal } : void 0
        )
      }
    }
  }
  return r
}

effect

export function effect<T = any>(
  fn: () => T,
  options: ReactiveEffectOptions = EMPTY_OBJ // 默认为空 更新组件的effect会用到
): ReactiveEffect<T> {
  // computed -> false
  if (isEffect(fn)) {
    fn = fn.raw
  }
  const effect = createReactiveEffect(fn, options)

  // 立即执行
  if (!options.lazy) {
    effect()
  }

  return effect
}

createReactiveEffect

function createReactiveEffect<T = any>(
  fn: (...args: any[]) => T,
  options: ReactiveEffectOptions
): ReactiveEffect<T> {
  const effect = function reactiveEffect(...args: unknown[]): unknown {

    if (!effect.active) {
      return options.scheduler ? undefined : fn(...args)
    }

    // effectStack为effect文件的全局属性,如果该effect已经在栈中等待执行,那么跳过 
    if (!effectStack.includes(effect)) {
      cleanup(effect) // 每次运行effect 都会清空对应的所有追踪 为了避免重复追踪
      try {
        // 允许追踪当前effect  
        enableTracking()
        // 入栈  
        effectStack.push(effect)

        // activeEffect为effect文件中的全局属性,表示当前正常执行的effect
        // 能够使响应式数据进行track追踪
        activeEffect = effect

        return fn(...args) // 返回执行的函数的结果
      } finally {
        // 出栈  
        effectStack.pop()

        // 恢复上一次effect信息,即本次允许追踪信息出栈 
        resetTracking()
        activeEffect = effectStack[effectStack.length - 1]
      }
    }
  } as ReactiveEffect

  effect.id = uid++ // 组件更新的时候 会根据id从小到大排序
  effect._isEffect = true
  effect.active = true
  effect.raw = fn
  effect.deps = []
  effect.options = options
  return effect
}

追踪过程(track)

在创建effect的时候,createReactiveEffect会设置activeEffect = effect,表示当前执行的effect,运行传递进effect的Function:

// Function:
() => {
  console.log(a.foo)
  console.log(rf.value)
}

a被设置成Proxy,可以监听赋值和取值等操作的行为,此处取值键为foo,查看内部getter。(需要注意的是track方法)

getter:

/*
  @target 目标对象
  @key 对象参数
  @receiver 自身Proxy
*/
function get(target: object, key: string | symbol, receiver: object) {
    // 特定Key相关s's
    if (key === ReactiveFlags.isReactive) {
      return !isReadonly
    } else if (key === ReactiveFlags.isReadonly) {
      return isReadonly
    } else if (
      key === ReactiveFlags.raw &&
      receiver ===
        (isReadonly
          ? (target as any).__v_readonly
          : (target as any).__v_reactive)
    ) {
      return target
    }

    // 数组相关
    const targetIsArray = isArray(target)
    if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
      // 拦截 ['includes', 'indexOf', 'lastIndexOf'] 方法 每一个遍历的 返回一个methods
      return Reflect.get(arrssayInstrumentations, key, receiver)
    } 

    // 这个其实就相当于 new Proxy() 返回target[key]
    const res = Reflect.get(target, key, receiver)

    // 获取原型
    if ((isSymbol(key) && builtInSymbols.has(key)) || key === '__proto__') {
      return res
    }

    // 非readonly 初始创建的时候的值 进行追踪,这里的重点
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }

    // shallow 初始创建的时候的值
    if (shallow) {
      return res
    }

    // 转换
    if (isRef(res)) {
      // ref unwrapping, only for Objects, not for Arrays.
      return targetIsArray ? 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
      // 如果是链式调用 检查到__v_reactive 就跳过处理 track 也是会执行的
      // readonly 就 readonly 递归循环
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }

track:

function track(target: object, type: TrackOpTypes, key: unknown) {
  if (!shouldTrack || activeEffect === undefined) {
    return
  }
  // WeakMap
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  let dep = depsMap.get(key) // value
  if (!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  // cleanup(effect) 每次执行trigger effect的时候 都会删除dep
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
    }
  }
}

询问targetMap有没有该target(引用属性),没有则设置target为键,value为空的Map,depsMap。

查看value存在不存在key,不存在则创建一个空的Set,dep。

查看dep存在不存在effect,不存在则存放当前activeEffect,給当前activeEffect.deps.push(dep)。

一个全局收集器收集target,key,activeEffect,由于追踪响应行为的是key,所以在key的层级存放activeEffect。

// 如果用对象来描述Map 用数组描述Set,那么以下就是当前测试变量a得出的结果
{ target: { key: [ activeEffect ] } }

触发过程(trigger)

setter:

function createSetter(shallow = false) {
  return function set(
    target: object, // 目标
    key: string | symbol, // Key
    value: unknown, // 设置的值
    receiver: object // receiver就是它的Proxy
  ): boolean {
    const oldValue = (target as any)[key]
    if (!shallow) { // shallow 浅
      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
    }

    // 如果没有hasOwn 则调用的是TriggerOptypes.Add 标记是set还是add
    const hadKey = hasOwn(target, key)

    // target直接赋值 这个操作不会再次触发setter
    const result = Reflect.set(target, key, value, receiver)

    // 如果target是原型链中的某个东西,就不要触发
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        // 触发器trigger
        trigger(target, TriggerOpTypes.ADD, key, value)
      } else if (hasChanged(value, oldValue)) {
        // 触发器trigger  
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    return result
  }
}

是不是看不懂为什么最后要target === toRaw(receiver) 才触发?

// 来调试一下你就知道啦
const ob1 = reactive({ foo: 2 })
const ob2 = reactive({ foo: 2 })

Object.setPrototypeOf(ob1, ob2)
ob1.a = 99

ob1.a的第一次setter,跑到:

 const result = Reflect.set(target, key, value, receiver)

这引用别人的一个例子:

blog

运行在浏览器上:

blog-change

再来看看Reflect.set的文档:

img

熊猫紧张啊这... 虽然我不懂,但是这不影响我去分析它:

第一次触发Reflect.set的过程会调用到原型中的set,等于触发parentProxy的

set(原本的target, Reflect.set传入的key, Reflect.set传入的value,Reflect.set传入的receiver)

第二次的Reflect.set,如果我把这行代码去掉,那么childProxy将不会被设置成功,只有这行代码才是真正设置childProxy的,但是对parentProxy没有任何影响,继续分析。

代码在set中,只返回true,或者在第二次Reflect中直接return true,也是不会有任何设置的成功,所以和返回的值无关。

第一次和第二的Reflect只有target是不一样的。

wocao那我第一次的Reflect.set随便丢一个target是不是会设置成功??

img

果真成功了....

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
  }

  const effects = new Set<ReactiveEffect>() // 收集当前target要触发的effect
  const computedRunners = new Set<ReactiveEffect>()

  // 添加要触发的effect
  const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
    if (effectsToAdd) {
      effectsToAdd.forEach(effect => {
        // activeEffect 和 shouldTrack 都是effect.ts 的全局值
        if (effect !== activeEffect || !shouldTrack) {
          if (effect.options.computed) {
            computedRunners.add(effect) // computed相关
          } else {
            effects.add(effect)
          }
        } else {
          // 不能是运行中的effect 或者 当前effect不能追踪
          // 因为 effect(() => { reactiveData.foo++ })
          // 首先track,赋值操作trigger,再触发track,死循环  
        }
      })
    }
  }

  if (type === TriggerOpTypes.CLEAR) {
    // 使用Map Set WeakMap WeakSet类型的响应式数据使用clear方法,才会触发
    // 测试用例在reactivity/__test__/collections/Map.sepc.ts 
    // 全局搜索 'should observe size mutations'
    depsMap.forEach(add)
  } else if (key === 'length' && isArray(target)) {
    // taregt是数组的情况下 我们修改其length长度,是一种修改操作
    depsMap.forEach((dep, key) => {
      // 缩短长度的操作等于删除元素 触发effect
      // 追踪过length的effect 都需要触发
      if (key === 'length' || key >= (newValue as number)) {
        add(dep)
      }
    })
  } else {
    // 使用void 0 代替 undefinded 因为undefinded又可能被重写  
    if (key !== void 0) {
      add(depsMap.get(key))
    }

    // iteration key on ADD | DELETE | Map.SET
    const isAddOrDelete =
      type === TriggerOpTypes.ADD ||
      (type === TriggerOpTypes.DELETE && !isArray(target))
    if (
      isAddOrDelete ||
      (type === TriggerOpTypes.SET && target instanceof Map)
    ) {
      add(depsMap.get(isArray(target) ? 'length' : ITERATE_KEY))
    }
    if (isAddOrDelete && target instanceof Map) {
      add(depsMap.get(MAP_KEY_ITERATE_KEY))
    }
  }

  const run = (effect: ReactiveEffect) => {
    if (__DEV__ && effect.options.onTrigger) {
      effect.options.onTrigger({
        effect,
        target,
        key,
        type,
        newValue,
        oldValue,
        oldTarget
      })
    }
    if (effect.options.scheduler) { // 渲染processComponent的时候在setupRenderEffect方法中,instance.update = effect(()=>{}, prodEffectOptions)
      effect.options.scheduler(effect)
    } else {
      effect()
    }
  }

  // effect.options.computed 类型的优先级比较高
  computedRunners.forEach(run)
  effects.forEach(run)
}

总结

img不总结了,就这么简单。

results matching ""

    No results matching ""