Notas del código fuente de aprendizaje de vue3 (serie introductoria para principiantes) ------ ¡Puntos clave! Análisis línea por línea del código de principio responsivo

Observación

Este artículo solo cubrirá la actualización del proceso principal de configuración, el reloj calculado, etc., que se analizará más adelante. Si
tiene preguntas, vaya al código fuente para encontrar respuestas.
¿Cuándo se crearon los datos reactivos?
¿Cuándo se realiza la recopilación de dependencias?
¿Cómo distribuir actualizaciones después de la actualización de datos responsivos?

Los casos de prueba utilizados en este artículo
deben depurarse; de ​​lo contrario, será fácil confundirse si sigue la depuración.

 it('should support runtime template compilation', async () => {
    
    
    const container = document.createElement('div')
    container.classList.add('app')
    const child = defineComponent({
    
    
      template: `
         <div><p>{
     
     {age}}---{
     
     {status?add:'hihi'}}</p></div>
      `,
      props:{
    
    
        age:{
    
    
          type: Number,
          default:20
        }
      },
      data(){
    
    
        return {
    
    
          add: '12',
          status: true
        }
      },
      mounted() {
    
    
          this.status = false
          this.add = '24'
      },
  })

    const App = {
    
    
      components:{
    
    child},
      beforeMount() {
    
    
        console.log('beforeMount');
        
      },
      data() {
    
    
        return {
    
    
        }
      },
      setup() {
    
    
        const count = ref(1)

        const age = ref('20')

        const obj  = reactive({
    
    name:'ws',address:'usa'})

        onMounted(()=>{
    
    
          obj.name = 'kd'
          count.value = 5
          age.value = '2'
        })

 
        return ()=>{
    
    
          return  h('div',[obj.name,h(child,{
    
    age:age.value})])
        }
      }
    }

  
    createApp(App).mount(container)
    await nextTick()
   
     expect(container.innerHTML).toBe(`0`)
  })

Creación de datos responsivos

¿Aún recuerdas en qué etapa se ejecuta la configuración de inicialización en el artículo anterior?

patch
processComponent
mountComponent
 // packages/runtime-dom/src/renderer.ts
  patch 阶段 组件首次挂载时
// mountComponent 方法
 
1. 先创建 组件 instance 实例
2. 初始化 setup props 等属性
3. 设置并运行带副作用的渲染函数

Cuando se inicializa la configuración, se crearán datos receptivos.
La función de configuración en el componente de la aplicación se ejecutará primero en el caso de prueba.

 setup() {
    
    
        // 会创建一个 ref 响应式数据
        const count = ref(1)
        // 会创建一个 ref 响应式数据
        const age = ref('20')
        // 会创建一个 reactive 响应式数据
        const obj  = reactive({
    
    name:'ws',address:'usa'})

        onMounted(()=>{
    
    
          obj.name = 'kd'
          count.value = 5
          age.value = '2'
        })

 
        return ()=>{
    
    
          return  h('div',[obj.name,h(child,{
    
    age:age.value})])
        }
      }
    }

El papel central de ref y reactivo.

Primero hablemos de la conclusión:
es el puente para las actualizaciones de vistas basadas en datos. La recopilación de dependencias (captador) y las actualizaciones de envío (establecedor) están en él

No hay mucha diferencia entre ref y reactivo (el proxy no puede actuar como proxy para tipos de datos básicos, por lo que vue3 usa get set en la clase para realizar el trabajo del proxy. Los principios posteriores de actualización de distribución y recopilación de dependencias son básicamente los mismos como reactivo) A continuación solo se analizará el reactivo.

  1. Primero determine el tipo de objeto proxy y clasifíquelo.
function targetTypeMap(rawType) {
    
    
  switch (rawType) {
    
    
    case 'Object':
    case 'Array':
      return TargetType.COMMON
    case 'Map':
    case 'Set':
    case 'WeakMap':
    case 'WeakSet':
      return TargetType.COLLECTION
    default:
      return TargetType.INVALID
  }
}
  1. Elija diferentes métodos getter-setter según las diferentes categorías.
    Analice solo los proxies de matriz de objetos más comunes.
export function reactive(target: object) {
    
    
  // if trying to observe a readonly proxy, return the readonly version.
  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>
) {
    
    
  // 省略。。。
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

Irá a baseHandlers, que es mutableHandlers

  1. Cómo funcionan los servidores proxy de datos en mutableHandlers

Veamos el núcleo de obtener primero, que se basa en el método de seguimiento de recopilación.

function createGetter(isReadonly = false, shallow = false) {
    
    
  return function get(target: Target, key: string | symbol, receiver: object) {
    
    
    // 对 ReactiveFlags 的处理部分
    if (key === ReactiveFlags.IS_REACTIVE) {
    
    
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
    
    
      return isReadonly
    } else if (key === ReactiveFlags.IS_SHALLOW) {
    
    
      return shallow
    } else if (
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
          ? shallowReactiveMap
          : reactiveMap
        ).get(target)
    ) {
    
    
      return target
    }

    const targetIsArray = isArray(target)

    if (!isReadonly) {
    
    
      // 数组的特殊方法处理
      if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
    
    
        return Reflect.get(arrayInstrumentations, key, receiver)
      }
      // 对象 hasOwnProperty 方法处理
      if (key === 'hasOwnProperty') {
    
    
        return hasOwnProperty
      }
    }

    // 取值
    const res = Reflect.get(target, key, receiver)
    // Symbol Key 不做依赖收集
    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
    
    
      return res
    }
    // 进行依赖收集
    if (!isReadonly) {
    
    
      track(target, TrackOpTypes.GET, key)
    }

    // 一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露
    if (shallow) {
    
    
      return res
    }

    if (isRef(res)) {
    
    
      //跳过数组、整数 key 的展开
      // ref unwrapping - skip unwrap for Array + integer key.
      return targetIsArray && isIntegerKey(key) ? 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 是 对象 且不是 readonly 就继续处理成 reactive
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}

rastrear la recopilación de dependencias

export 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
    // 将 activeEffect 存入到 dep 同时将 dep[] 存入到 activeEffect 中 deps 属性 上 
    trackEffects(dep, eventInfo)
  }
}



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. 判断现在有没有activeEffect  有activeEffect才发生依赖收集
    // activeEffect 每个组件初始化的时候会有一个activeEffect 
    // 这一步的作用 是为了避免多余的依赖收集 例如在setup 创建了 响应式数据 同步 访问 或者 修改 这个数据 这时候 都不会发生 依赖收集。只会在 执行render函数的时候 才发生依赖收集 
    shouldTrack = !dep.has(activeEffect!)
  }
  
  if (shouldTrack) {
    
    
    dep.add(activeEffect!)
    activeEffect!.deps.push(dep)
  }
}


código central de efecto reactivo

// 用于记录位于响应上下文中的effect嵌套层次数
let effectTrackDepth = 0
// 二进制位,每一位用于标识当前effect嵌套层级的依赖收集的启用状态
export left trackOpBit = 1
// 表示最大标记的位数
const maxMarkerBits = 30

// 当前活跃的 effect
let activeEffect;

export class ReactiveEffect {
    
    
  // 用于标识副作用函数是否位于响应式上下文中被执行
  active = true
  // 副作用函数持有它所在的所有依赖集合的引用,用于从这些依赖集合删除自身
  deps = []
  // 指针为,用于嵌套 effect 执行后动态切换 activeEffect
  parent = undefined
  // ...
  run() {
    
    
    // 若当前 ReactiveEffect 对象脱离响应式上下文
    // 那么其对应的副作用函数被执行时不会再收集依赖
    if (!this.active) {
    
    
      return this.fn()
    }
    
    // 缓存是否需要收集依赖
    let lastShouldTrack = shouldTrack
    
    try {
    
    
      // 保存上一个 activeEffect 到当前的 parent 上
      this.parent = activeEffect
      // activeEffect 指向当前的 effect
      activeEffect = this
      // shouldTrack 置成 true
      shouldTrack = true
      // 左移操作符 << 将第一个操作数向左移动指定位数
      // 左边超出的位数将会被清除,右边将会补零。
      // trackOpBit 是基于 1 左移 effectTrackDepth 位
      trackOpBit = 1 << ++effectTrackDepth
      
      // 如果未超过最大嵌套层数,则执行 initDepMarkers
      if (effectTrackDepth <= maxMarkerBits) {
    
    
        initDepMarkers(this)
      } else {
    
    
        cleanupEffect(this)
      }
      // 这里执行了 fn
      return this.fn()
    } finally {
    
    
      if (effectTrackDepth <= maxMarkerBits) {
    
    
        // 用于对曾经跟踪过,但本次副作用函数执行时没有跟踪的依赖采取删除操作。
        // 新跟踪的 和 本轮跟踪过的都会被保留
        finalizeDepMarkers(this)
      }
      
      // << --effectTrackDepth 右移动 effectTrackDepth 位
      trackOpBit = 1 << --effectTrackDepth
      
      // 返回上个 activeEffect
      activeEffect = this.parent
      // 返回上个 shouldTrack
      shouldTrack = lastShouldTrack
      // 情况本次的 parent 指向
      this.parent = undefined
    }
  }
}

Insertar descripción de la imagen aquí

ilustrar:

El efecto [] en depsMap se usa para ejecutar reactiveEffect en la matriz de efectos cada vez que se envía una actualización (en realidad llama al método de ejecución de la instancia de reactiveEffect)

En reactiveEffect, cuando se ejecuta el método de ejecución, el método initDepMarkers se agrega a cada objeto en la matriz deps para agregar el atributo w para indicar que se ha recopilado y procesado en la pista de recopilación de dependencias ----> se agregarán trackEffects al departamento en depsMap (este departamento y instancia de efecto Corresponden a cada objeto en departamentos) asigne n (lo que indica que se ha recopilado recientemente)

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 类型是 set<ReativeEffect>
        dep.delete(effect)
      } else {
    
    
        deps[ptr++] = dep
      }
      // clear bits
      dep.w &= ~trackOpBit
      dep.n &= ~trackOpBit
    }
    deps.length = ptr
  }
}

Cuando se ejecuta la función fn registrada en effect.run(), se llamará a finalizeDepMarkers para eliminar los efectos que no se han recopilado en esta ronda de depósito para evitar la activación innecesaria de la lógica de actualización.

Luego casos de prueba para ilustrar
Insertar descripción de la imagen aquí

¿Cuándo ocurre la primera ronda de recopilación de dependencias?

Al inicializar la función de efectos secundarios en el componente de la aplicación, primero se creará reactiveEffect y se montará en app.instance
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí

La aplicación activará activamente instancia.update() para montar el primer componente.
El capítulo anterior explicó el primer proceso de montaje del componente.
La llamada real es reactiveEffect.run ----> ejecutar componenteUpdateFn —> renderizar (generar subárbol) ----> parche ----> ProcessElement

Los datos de respuesta utilizados en el proceso de renderizado se recopilan de forma dependiente.
En este momento, esta ronda de recopilación de dependencias del componente de la aplicación se completa. Se utilizan Obj y age. En
Insertar descripción de la imagen aquí
este momento, se ejecuta el elemento de proceso del componente de la aplicación. Dado que hay un subcomponente secundario, se ejecuta mountChild ----> parche -> el componente de proceso del componente secundario. El componente secundario también será el mismo que el componente de la aplicación. Inicialice la instancia, cree ReactiveEffect, active la actualización, ejecute el componenteUpdateFn que pertenece al componente secundario y luego ejecute la función de renderizado del componente secundario. El componente secundario recopilará dependencias.

Insertar descripción de la imagen aquí
estado de edad agregar

Esta ronda de recopilación de dependencias está completa.

总结:
组件的首次依赖收集 发生在 render阶段 顺序是 父组件 setup---->父组件 render ---->子组件 setup 
----> 子组件render

¿Se producirá una recopilación de dependencias si los datos reactivos se cambian durante la fase de configuración?

例如:
setup(){
    
    
   const age = ref(20)
   // 这里发生了访问操作
   const temp = age.value
   return ()=>{
    
    
      return h('div',[age.value]) 
   }
}

这时候会触发响应式数据的 get 操作 
但是由于 没有 activeEffect(这时候 组件还没开始设置副作用函数(SetupRenderEffectFn)所以没有activeEffect) 所以不会发生依赖收集 

扩展:
setup(){
    
    
   const age = ref(20)
   setTimeout(()=>{
    
    
   // 这里发生了访问操作
      console.log(age.value);  
   })
   return ()=>{
    
    
      return h('div',[age.value]) 
   }
}
这时候 也会触发响应式数据的 get 操作 ,也是没有activeEffect(组件已经完成 effect.run 方法了,这时候 activeEffect 已经被置为空) 所以也不会发生依赖收集

后续:
在setup函数之后的生命周期(如mounted、updated等钩子函数)中访问响应式数据会触发依赖收集 (后面再分析)

Distribuir actualizaciones

¿Cuándo se activa la actualización de la distribución?

Después de montar el componente anterior, la operación de datos de respuesta modificados que escribí en el gancho del ciclo de vida activado activará el configurador. Echemos un vistazo al código fuente del configurador reactivo.


function createSetter(shallow = false) {
    
    
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    
    
    // 。。。 省略部分逻辑
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    const result = Reflect.set(target, key, value, receiver)
    // 如果target是原型链上的东西,不要触发
    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
  }
}


export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
    
    
  // 根据 target 查到对应的 depsMap
  const depsMap = targetMap.get(target)
  // 不存在depsMap 不触发更新
  if (!depsMap) {
    
    
    // never been tracked
    return
  }
  
  // 用于 暂存 effect
  let deps: (Dep | undefined)[] = []
  if (type === TriggerOpTypes.CLEAR) {
    
    
    // collection being cleared
    // trigger all effects for target
    deps = [...depsMap.values()]
  } else if (key === 'length' && isArray(target)) {
    
    
    const newLength = Number(newValue)
    depsMap.forEach((dep, key) => {
    
    
      if (key === 'length' || key >= newLength) {
    
    
        deps.push(dep)
      }
    })
  } else {
    
    
    // schedule runs for SET | ADD | DELETE
    if (key !== void 0) {
    
    
      deps.push(depsMap.get(key))
    }

    // also run for iteration key on 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)
      }
    }
    // 下面操作 是为了 去重 保证相同的effect 只会有一个
    if (__DEV__) {
    
    
      triggerEffects(createDep(effects), eventInfo)
    } else {
    
    
      triggerEffects(createDep(effects))
    }
  }
}


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))
    }
    // 最终会执行 scheduler 是在 初始化的时候 创建的
    if (effect.scheduler) {
    
    
      effect.scheduler()
    } else {
    
    
      effect.run()
    }
  }
}

 //  在SetupRenderEffectFn 阶段中 create reactive effect for rendering
    const effect = (instance.effect = new ReactiveEffect(
      componentUpdateFn,
      () => queueJob(update),// 这个就是 scheduler
      instance.scope // track it in component's effect scope
    ))

Resumen: cuando los datos de respuesta se actualizan y el depsMap correspondiente no está vacío, se activará la actualización del componente (la forma de actualizar se responderá en la siguiente pregunta)

Extensión: ¿La modificación de los datos de respuesta en la fase de configuración desencadenará actualizaciones de componentes?
setup(){
    
    
   const age = ref(20)
   // 修改操作
   age.value = 10
   return ()=>{
    
    
      return h('div',[age.value]) 
   }
}
会触发 setter 操作 由于 depsMap 为空 所以不会发生派发更新

¿Cómo activa vue la representación de actualizaciones de componentes en función de las actualizaciones de envío?

Insertar descripción de la imagen aquí
El núcleo del envío de actualizaciones es activar effect.scheduler (el método de escritura de componentes convencional es crear un programador para activeEffect)

const effect = (instance.effect = new ReactiveEffect(
      componentUpdateFn,
      () => queueJob(update), // effect.schedule
      instance.scope // track it in component's effect scope
    ))

Analizar cola de trabajo


export function queueJob(job: SchedulerJob) {
    
    
  // the dedupe search uses the startIndex argument of Array.includes() 确保不会重复设置 schedule
  // by default the search index includes the current job that is being run 默认包括正在运行的 schedule
  // so it cannot recursively trigger itself again. 避免递归触发自身再次运行
  // if the job is a watch() callback, the search will start with a +1 index to 运行在watch 中 重复运行
  // allow it recursively trigger itself - it is the user's responsibility to
  // 确保它不会陷入无限循环

  // 去重判断
  if (
    !queue.length ||
    !queue.includes(
      job,
      isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex
    )
  ) {
    
    
  //添加到队列尾部
    if (job.id == null) {
    
    
      queue.push(job)
    } else {
    
    
      // 按照 job id 自增的顺序添加 (一般父组件的id 要小于子组件 保证 父组件永远先于子组件触发更新)
      // 这个id 是由 instance.uid 决定 就是在初始化组件实例 确定( 具体代码 runtime-core/src/component) 先初始化的 uid(每次创建组件实例 全局 uid会加1) 会小,
      queue.splice(findInsertionIndex(job.id), 0, job)
    }
    queueFlush()
  }
}

// 通过promise.then 创建 微任务(去执行flushjob)
function queueFlush() {
    
    
  if (!isFlushing && !isFlushPending) {
    
    
    isFlushPending = true
    currentFlushPromise = resolvedPromise.then(flushJobs)
  }
}


function flushJobs(seen?: CountMap) {
    
    
  // 是否正在等待执行
  isFlushPending = false
  // 正在执行
  isFlushing = true


   // 在更新前,重新排序好更新队列 queue 的顺序
  // 这确保了:
  // 1. 组件都是从父组件向子组件进行更新的。(因为父组件都在子组件之前创建的
  // 所以子组件的渲染的 effect 的优先级比较低)
  // 2. 如果父组件在更新前卸载了组件,这次更新将会被跳过。
  queue.sort(comparator)

 

  try {
    
    
  // 遍历主任务队列,批量执行更新任务
    for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
    
    
      const job = queue[flushIndex]
      if (job && job.active !== false) {
    
    
        if (__DEV__ && check(job)) {
    
    
          continue
        }
        // 这个 job 就是 effect.run
        callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
      }
    }
  } finally {
    
    
   // 队列任务执行完,重置队列索引
    flushIndex = 0
     // 清空队列
    queue.length = 0
    // 执行后置队列任务
    flushPostFlushCbs(seen)
    // 重置队列执行状态
    isFlushing = false
    // 重置当前微任务为 Null
    currentFlushPromise = null
    // 如果主任务队列、后置任务队列还有没被清空,就继续递归执行
    if (queue.length || pendingPostFlushCbs.length) {
    
    
      flushJobs(seen)
    }
  }
}


Resumen: las actualizaciones de envío activadas por el componente montado se recopilarán en una cola de tareas de ejecución de microtask. Después de ejecutar la macrotarea del proceso principal, se ejecutará la cola de tareas de microtask para comenzar a activar el trabajo de ejecución (effect.run -> updateComponentFn)

¿Cómo garantizar que el componente solo active una representación de actualización cuando hay múltiples actualizaciones de datos receptivos cuando se ejecuta la función de efecto secundario del componente?

Con el análisis del código fuente anterior, ya podemos obtener la respuesta de por qué múltiples actualizaciones de datos responsivos solo activan una actualización de componente cuando se ejecuta la función de efecto secundario de un componente
.

setup(){
    
    
   const num1 = ref(20)
   
   const num2 = ref(10)

   onMounted(()=>{
    
    
     num1.value = 40
     num1.value = 50
     num2.value = 100
   })
   

   return ()=>{
    
    
      return h('div',[num1.value+num2.value]) 
   }
}

Después de actualizar tres veces en onMounted y activar triggerEffect tres veces, se colocarán tres operaciones de actualización en la microtarea. Dado que el job.id entrante es el mismo, solo se creará un componente de tarea de actualización en la cola de actualización y solo se actualizará. una vez.

¿Cómo se eliminan las dependencias de componentes redundantes?

Después de cada renderizado, el componente limpiará los efectos posteriores que no se hayan recopilado (correspondiente al efecto reactivo en el depósito (conjunto) correspondiente a cada dato de respuesta)

ejemplo:

const child = defineComponent({
    
    
      template: `
         <div><p>{
     
     {age}}---{
     
     {status?add:'hihi'}}</p></div>
      `,
      props:{
    
    
        age:{
    
    
          type: Number,
          default:20
        }
      },
      data(){
    
    
        return {
    
    
          add: '12',
          status: true
        }
      },
      mounted() {
    
    
          this.status = false
          this.add = '24'
      },
  })
// mounted 阶段 改变了 status 触发了 组件更新 重新 render 的 时候 会发生新的一轮依赖收集 
// 之前 组件 是有两个 dep 一个 属于 status 一个属于 add 但是,由于新的依赖收集 add 不会被用到 所以 在 effect.run 执行完 后 add 的 dep 会被清除掉 是根据 dep 赋值的 w 和 n 属性 去比较

Supongo que te gusta

Origin blog.csdn.net/weixin_45485922/article/details/132754955
Recomendado
Clasificación