vue3源码解析--effect

205 阅读2分钟

说明

  • 本文主要探讨createDep、initDepMarkers、finalizeDepMarkers、wasTracked、newTracked方法中dep.n、dep.w变化,省略了非必要的代码

调试源码用例

        import { reactive, effect,readonly,isReactive } from './reactivity.esm-browser.js'
        const state = reactive({flag: true, age:10,name:'wl'})
        effect(()=>{
          state.flag ? state.age: state.name;
        })
        state.flag = false;

关键步骤

effect 函数
export function effect(fn) {
  const _effect = new ReactiveEffect(fn)
   _effect.run()
}
run函数
 run() {
    try {
      this.parent = activeEffect
      activeEffect = this
      trackOpBit = 1 << ++effectTrackDepth
      if (effectTrackDepth <= maxMarkerBits) { 
        initDepMarkers(this)
      }
      // 开始执行fn,开始进行依赖手机
      return this.fn()
    } finally {
      if (effectTrackDepth <= maxMarkerBits) {
        finalizeDepMarkers(this)
      }
      trackOpBit = 1 << --effectTrackDepth
      activeEffect = this.parent
      this.parent = undefined
    }
  }
initDepMarkers函数
export const initDepMarkers = ({ deps }) => {
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].w |= trackOpBit // set was tracked
    }
  }
}
track函数
export function track(target, type, key) {
  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()))
    }
    trackEffects(dep)
  }
}
trackEffects 函数
export function trackEffects(dep) {
  let shouldTrack = false
  if (effectTrackDepth <= maxMarkerBits) {
    if (!newTracked(dep)) {
      dep.n |= trackOpBit // set newly tracked
      shouldTrack = !wasTracked(dep)
    }
  }
  if (shouldTrack) {
    dep.add(activeEffect!)
    activeEffect!.deps.push(dep)
  }
}
export const newTracked = (dep: Dep): boolean => (dep.n & trackOpBit) > 0
export const wasTracked = (dep: Dep): boolean => (dep.w & trackOpBit) > 0
  • _effect.run()执行run方法,effectTrackDepth为0的全局变量,trackOpBit = 1 << ++effectTrackDepth后,trackOpBit是2 (base 10),00000000 00000000 00000000 00000010 (base 2) 接着执行initDepMarkers(this),此时的this.deps.length ===0 ,就直接跳过,开始执行this.fn进行依赖收集. 会惊醒收集flag,age 这个连个属性。
  • state.flag取值,接着执行track方法,初始化会执行createDep 创建dep,此时的dep.w === 0,dep.n ===0.
  • 执行trackEffects(dep),执行newTracked(dep),dep.n === 0, newTracked(dep)返回false。注意:effect回调中用到属性都会进入这个判断分支。同理,此时wasTracked(dep)也是false,shouldTrack为true.
  • 总结:此时的state.flag对应的dep.n = 2 即0000 0010, dep.w = 0即0000 0000.同理state.age对应的dep.n = 2 即(0000 0010),dep.w = 0即 0000 0000.此activeEffect.deps= [dep1,dep2] dep1,dep2分别是flag,age对应的dep.
  • this.fn执行完,执行finalizeDepMarkers(this),wasTracked(dep)返回false,
  • 总结:此时两个dep中, dep.n dep.w 都是0. activeEffect.deps= [dep1,dep2]
export const finalizeDepMarkers = (effect: ReactiveEffect) => {
  const { deps } = effect
  if (deps.length) {
    let ptr = 0
    for (let i = 0; i < deps.length; i++) {
      const dep = deps[i]
      if (wasTracked(dep) && !newTracked(dep)) {
        dep.delete(effect)
      } else {
        // 第一次执行run走这儿,wasTracked(dep)是false
        deps[ptr++] = dep
      }
      //清理 was 和 new 标记,将它们对应深度的 bit,置为 0
      // clear bits
      // trackOpBit是0000 0010 
      // ~trackOpBit 1111 1101 
      // dep.w       0000 0000 
      // dep.n       0000 0010
      // 所以 dep.w &= ~trackOpBit执行后 dep.w 0000 0000 dep.n 0000 0000
      dep.w &= ~trackOpBit
      dep.n &= ~trackOpBit
    }
    deps.length = ptr
    // 到此一个effect的依赖手机完成
  }
}

改变flag为false

export function triggerEffects(dep) {
  const effects = isArray(dep) ? dep : [...dep]
  for (const effect of effects) {
      triggerEffect(effect)
  }
}
function triggerEffect(effect) {
   effect.run()
}
export const initDepMarkers = ({ deps }: ReactiveEffect) => {
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].w |= trackOpBit // set was tracked
    }
  }
}
  • 再次执行run方法,trackOpBit = 1 << ++effectTrackDepth后,trackOpBit是2 (base 10),00000000 00000000 00000000 00000010 (base 2) ,执行initDepMarkers(this),将activeEffect.deps= [dep1,dep2]中的两个dep的dep.w = 2
  • 执行this.fn收集依赖,此时收集的flag,name两个属性,
  • 收集flag时,此时targetMap中存储着flag对应的dep,不需要执行createDep,接着执行下边代码.执行完下边代码shouldTrack是false,不用在进行依赖收集,即flag在更新前后都在effect中使用
  • 此时flag对应的dep.w === 2,dep.n === 2
  • 收集name的依赖,targetMap中不存在name对应的dep,创建性的dep即dep.n === 0、dep.w === 0,执行代码块1后,name对应的dep.n===2,shouldTrack=== true,将此dep添加到activeEffect.deps中。
  • 总结:此时activeEffect.deps= [dep1,dep2,dep3]
  • dep1: 对应flag dep.n === 2 dep.w === 2
  • dep2: 对应age dep.n === 0 dep.w === 2
  • dep3: 对应name dep.n ===2 dep.w === 0
  • 执行完this.fn,执行finalizeDepMarkers,此时age对应的dep中删除此activeEffect,重置dep.n === dep.w ===0.
  • name对应的dep覆盖activeEffect.deps[1]中元素,重置dep.n === dep.w ===0.
  • 总结:此时activeEffect.deps= [dep1,dep3]
    if (!newTracked(dep)) { // flag的dep.n === 0,newTracked(dep)返回false
      dep.n |= trackOpBit // dep.n === 2
      shouldTrack = !wasTracked(dep) 
      // dep.w === 2,trackOpBit也是2,wasTracked(dep)返回true,shouldTrack是false
    }

总结:dep.n、dep.w变化过程

  • 初始化 | 标题 | flag dep1| age dep2 | name dep3 | activeEffect.deps | | --- | --- | --- | --- | ---| | 初始化 | n:0 w:0 | n:0 w:0 | | | this.fn后 | n:2 w:0 | n:2 w:0 | | [dep1,dep2] | | finalizeDepMarkers | n:0 w:0 | n:0 w:0 | | [dep1,dep2] |
  • 改变flag再次执行run | 标题 | flag dep1| age dep2 | name dep3 | activeEffect.deps | | --- | --- | --- | --- | ---| | initDepMarkers | n:0 w:2 | n:0 w:2 | | dep1,dep2] | | this.fn后 | n:2 w:2 | n:0 w:2 | n:2 w:0 | [dep1,dep2,dep3] | | finalizeDepMarkers | n:0 w:0 | n:0 w:0 | n:0 w:0 | [dep1,dep3] |
  • age的dep2中删除此activeEffect。