Rumah  >  Artikel  >  hujung hadapan web  >  Kemas kini asynchronous komponen Vue3 dan analisis kod sumber mekanisme yang dijalankan nextTick

Kemas kini asynchronous komponen Vue3 dan analisis kod sumber mekanisme yang dijalankan nextTick

WBOY
WBOYke hadapan
2023-05-16 10:01:131090semak imbas

Kemas kini komponen tak segerak

Kita semua sepatutnya tahu atau pernah mendengar bahawa kemas kini komponen adalah tidak segerak Untuk nextTick, kami juga tahu bahawa ia menggunakan janji untuk meletakkan fungsi panggil balik masuk ke dalam baris gilir microtask dilaksanakan selepas fungsi dikemas kini Jadi memandangkan semuanya adalah kemas kini tak segerak, bagaimanakah nextTick memastikan bahawa panggilan balik akan dilaksanakan selepas komponen dikemas kini, dan bilakah masa untuk memasukkannya ke dalam baris gilir? Dengan soalan ini kami pergi ke kod sumber untuk mencari jawapan.

Mari kita semak dahulu kesan kemas kini komponen:

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
))

Apabila data responsif berubah dan mencetuskan pelaksanaan kesan, penjadual () => queueJob(update) akan dilaksanakan, jadi kita perlu melihat apa queueJob melakukan

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 terutamanya menambahkan penjadual pada baris gilir, dan kemudian melaksanakan 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)
  }
}

flushJob Apabila semua Selepas penjadual penyegerakan dilaksanakan, ia akan memproses tugas dalam baris gilir microtask dan melaksanakan fungsi panggil balik flushJobs

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)
    }
  }
}

Ringkasan:

Apabila data responsif diubah suai dalam komponen, Fungsi kemas kini komponen akan diletakkan dalam baris gilir, dan kemudian tugas mikro didaftarkan ini bertanggungjawab untuk melaksanakan semua kerja dalam baris gilir, jadi walaupun kita mengubah suai berbilang/berbilang data responsif secara serentak, fungsi kemas kini komponen yang sama hanya akan. dimasukkan ke dalam Sebaik sahaja dalam baris gilir, microtask berdaftar akan dilaksanakan hanya selepas operasi penyegerakan selesai, fungsi kemas kini komponen akan dilaksanakan, dan komponen akan dikemas kini.

nextTick

Pelaksanaan nextTick dalam vue3 adalah sangat mudah:

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
}

Kunci di sini ialah currentFlushPromise Jika anda cukup berhati-hati, anda akan mendapati currentFlushPromise sebenarnya ditetapkan dalam queueFlush. , ia adalah janji yang meletakkan tugas melaksanakan fungsi kemas kini komponen ke dalam baris gilir mikro, jadi di sini kita mendapat currentFlushPromise dan hanya meletakkan panggilan balik fungsi yang diterima oleh nextTick di belakang flushJobs dalam baris gilir mikro selesai, komponen akan Kemas kini telah selesai Inilah masanya kita mahu melaksanakan panggilan balik nextTick!

Atas ialah kandungan terperinci Kemas kini asynchronous komponen Vue3 dan analisis kod sumber mekanisme yang dijalankan nextTick. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam