Vue3 구성 요소 비동기 업데이트 및 nextTick 실행 메커니즘 소스 코드 분석

WBOY
풀어 주다: 2023-05-16 10:01:13
앞으로
1109명이 탐색했습니다.

구성 요소의 비동기 업데이트

우리는 구성 요소 업데이트가 비동기적이라는 것을 모두 알고 있거나 들어야 합니다. nextTick의 경우 Promise를 사용하여 수신 콜백 함수를 마이크로태스크 대기열에 넣고 함수가 실행된 후에 실행한다는 것도 알고 있습니다. 업데이트되었습니다. 그러면 모두 비동기 업데이트이므로 nextTick은 구성 요소가 업데이트된 후 콜백이 실행되도록 어떻게 보장하며 이를 대기열에 삽입하는 타이밍은 언제입니까? 이러한 질문에 대한 답을 찾기 위해 소스 코드로 이동합니다.

먼저 구성요소 업데이트의 효과를 검토합니다.

const effect = (instance.effect = new ReactiveEffect(
  componentUpdateFn,
  () => queueJob(update), // updata: () => effect.run() 返回值 componentUpdateFn
  // 将effect添加到组件的scope.effects中
  instance.scope // track it in component's effect scope
))
로그인 후 복사

반응형 데이터가 변경되고 효과 실행이 트리거되면 () => queueJob(update)scheduler가 실행되므로 queueJob이 수행하는 작업을 확인해야 합니다.

queueJob

// packages/runtime-core/src/scheduler.ts
export function queueJob(job: SchedulerJob) {
  if (
    !queue.length ||
    !queue.includes( // queue中是否已经存在相同job
      job,
      isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex
    )
  ) {
    if (job.id == null) {
      // 向queue添加job queue是一个数组
      queue.push(job)
    } else {
      queue.splice(findInsertionIndex(job.id), 0, job)
    }
    // 执行queueFlush
    queueFlush()
  }
}
로그인 후 복사

queueJob이 주로 스케줄링합니다. 스케줄러를 대기열에 추가한 후 queueFlush

queueFlush

function queueFlush() {
  // isFlushing和isflushPending初始值都是false
  // 说明当前没有flush任务在执行,也没有flush任务在等待执行
  if (!isFlushing && !isFlushPending) {
    // 初次执行queueFlush将isFlushPending设置为true 表示有flush任务在等待执行
    isFlushPending = true
    // resolvedPromise是promise.resolve()
    // flushJobs被放到微任务队列中 等待所有同步scheduler执行完毕后执行
    // 这样就可以保证flushJobs在一次组件更新中只执行一次
    // 更新currentFlushPromise 以供nextTick使用
    currentFlushPromise = resolvedPromise.then(flushJobs)
  }
}
로그인 후 복사

flushJobs

를 실행합니다. 모든 동기화 스케줄러가 실행되면 마이크로태스크 대기열의 작업이 처리되고, flashJobs 콜백 함수가 실행됩니다

function flushJobs(seen?: CountMap) {
  // 状态改为有flush正在执行
  isFlushPending = false
  isFlushing = true
  if (__DEV__) {
    seen = seen || new Map()
  }
  // Sort queue before flush.
  // This ensures that:
  // 1. Components are updated from parent to child. (because parent is always
  //    created before the child so its render effect will have smaller
  //    priority number)
  // 2. If a component is unmounted during a parent component's update,
  //    its update can be skipped.
  // 组件更新的顺序是从父到子 因为父组件总是在子组件之前创建 所以它的渲染效果将具有更小的优先级
  // 如果一个组件在父组件更新期间被卸载 则可以跳过它的更新
  queue.sort(comparator)
  ...
  // 先执行queue中的job 然后执行pendingPostFlushCbs中的job
  // 这里可以实现watch中的 postFlush
  try {
    for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
      const job = queue[flushIndex]
      if (job && job.active !== false) {
        if (__DEV__ && check(job)) {
          continue
        }
        // console.log(`running:`, job.id)
        // 执行job
        callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
      }
    }
  } finally {
    // job执行完毕后清空队列
    flushIndex = 0
    queue.length = 0
    // 执行flushPostFlushCbs 此时组件已经更新完毕
    flushPostFlushCbs(seen)
    isFlushing = false
    currentFlushPromise = null
    // some postFlushCb queued jobs!
    // keep flushing until it drains.
    if (queue.length || pendingPostFlushCbs.length) {
      flushJobs(seen)
    }
  }
}
로그인 후 복사

요약:

Component 반응형 데이터를 수정한 후 컴포넌트 업데이트 기능이 대기열에 배치되고 마이크로 태스크가 등록됩니다. 이 마이크로 태스크는 대기열의 모든 작업을 실행하므로 다중/다중 반응형 데이터를 수정하더라도 마찬가지입니다. 동시에, 동일한 구성 요소의 업데이트 기능은 대기열에 한 번만 들어가고 등록된 마이크로 태스크는 동기화 작업이 완료된 후에만 실행되며 구성 요소 업데이트 기능이 실행되고 구성 요소가 업데이트됩니다.

nextTick

vue3에서 nextTick을 구현하는 것은 매우 간단합니다.

export function nextTick<T = void>(
  this: T,
  fn?: (this: T) => void
): Promise<void> {
  const p = currentFlushPromise || resolvedPromise
  // nextTick回调函数放到currentFlushPromise的微任务队列中
  return fn ? p.then(this ? fn.bind(this) : fn) : p
}
로그인 후 복사

여기서 핵심은 currentFlushPromise입니다. 주의 깊게 살펴보면 currentFlushPromise가 실제로 queueFlush에 할당되어 있음을 알 수 있습니다. 마이크로 대기열에 Promise를 입력하므로 여기서는 currentFlushPromise를 가져오고 플러시Jobs 뒤에 있는 nextTick 함수 콜백을 마이크로 대기열에 넣습니다. 이때 구성 요소가 업데이트됩니다. nextTick 콜백의 타이밍을 실행하려면!

위 내용은 Vue3 구성 요소 비동기 업데이트 및 nextTick 실행 메커니즘 소스 코드 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:yisu.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿