$nextTick源码解析

本文详细解释了Vue.js内置的nextTick方法的工作原理,它如何在DOM更新循环结束后执行回调,涉及Promise、MutationObserver和setTimeout的使用策略。
摘要由CSDN通过智能技术生成

this.$nextTick 是 Vue.js 内部使用的一个方法,用于在下一个 DOM 更新循环结束之后执行回调函数。

原理:

  1. nextTick 方法被调用后,会将回调函数存储在一个队列中
  2. Vue.js 会利用浏览器的异步队列机制,在 DOM 更新循环结束后执行这个队列中的所有回调函数。

源码:

/**
 * Defer a task to execute it asynchronously.
 */
export const nextTick = (function () {
  const callbacks = []
  let pending = false
  let timerFunc

  function nextTickHandler () {
    pending = false
    const copies = callbacks.slice(0)
    callbacks.length = 0
    for (let i = 0; i < copies.length; i++) {
      copies[i]()
    }
  }

  // 判断浏览器是否支持Promise,如果支持,则使用Promise的方式实现nextTick
  if (typeof Promise !== 'undefined' && isNative(Promise)) {
    const p = Promise.resolve()
    timerFunc = () => {
      p.then(nextTickHandler)
    }
  } else if (!isIE && typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]'
  )) {
    // Use MutationObserver where native Promise is not available,
    // e.g. PhantomJS, iOS7, Android 4.4
    let counter = 1
    const observer = new MutationObserver(nextTickHandler)
    const textNode = document.createTextNode(String(counter))
    observer.observe(textNode, {
      characterData: true
    })
    timerFunc = () => {
      counter = (counter + 1) % 2
      textNode.data = String(counter)
    }
  } else {
    // Fallback to setTimeout
    timerFunc = () => {
      setTimeout(nextTickHandler, 0)
    }
  }

  return function queueNextTick (cb?: Function, ctx?: Object) {
    let _resolve
    callbacks.push(() => {
      if (cb) {
        try {
          cb.call(ctx)
        } catch (e) {
          handleError(e, ctx, 'nextTick')
        }
      } else if (_resolve) {
        _resolve(ctx)
      }
    })
    if (!pending) {
      pending = true
      timerFunc()
    }
    // $flow-disable-line
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        _resolve = resolve
      })
    }
  }
})()

在这个方法中,首先定义了一个 callbacks 数组用于存储回调函数,以及一个 pending 标志用于判断是否有回调函数还未执行。然后根据浏览器支持的异步机制,决定使用哪一种方式来实现 nextTick。

如果浏览器支持 Promise,就使用 Promise 的方式,创建一个 resolved 状态的 Promise 对象 p,并利用 p 的 then 方法在下一个 microtask 中执行 nextTickHandler 方法。

如果浏览器支持 MutationObserver,也是创建一个 promise,然后通过改变文本节点的值来触发 MutationObserver,并在触发回调函数时执行 nextTickHandler 方法。

如果浏览器都不支持,则回退到使用 setTimeout。

最后,通过闭包返回一个函数 queueNextTick,该函数接受一个回调函数 cb 和一个上下文对象 ctx,将回调函数包装成一个新的函数,并将其推入 callbacks 数组中。如果当前没有其他回调函数正在执行,则通过 timerFunc 来触发回调函数的执行。

如果在调用 nextTick 时没有传入回调函数,则返回一个 promise 对象。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值