vue2/vue3 中nextTick的作用和源码解读

一、Vue2 的nextTick作用和源码解读

官网解释:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

修改Vue中的响应数据后触发界面更新这时一个异步的过程,使用同步操作是无法获取到正确的DOM结构/状态

源码

// 定义了一个全局变量 isUsingMicroTask 用于标识是否正在使用微任务
let isUsingMicroTask = false
// 创建了一个空数组 callbacks 用于存储待执行的回调函数。
const callbacks = []
//表示是否有待执行的回调函数
let pending = false

// 用于执行存储在 callbacks 数组中的回调函数
function flushCallbacks() {
  pending = false
  // 复制一份 callbacks 数组以防止在执行过程中数组被修改
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

接下来选择合适的定时器函数 timerFunc,用于执行 flushCallbacks

let timerFunc
// 检查环境中是否支持 Promise,并且该 Promise 是原生实现的 (微队列)
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  // 使用 Promise.resolve().then() 的方式来执行 flushCallbacks 函数
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
  }
  isUsingMicroTask = true
} 
//如果不支持 Promise,但支持 MutationObserver,(微队列)
else if (
  typeof MutationObserver !== 'undefined' &&
  (isNative(MutationObserver) ||
    MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
 	
  let counter = 1
  // 创建一个 MutationObserver 对象
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  // 以文本节点作为观察目标
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
  // 文本节点的内容发生变化时触发 flushCallbacks 函数。
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // 如果不支持 Promise 和 MutationObserver,但支持 setImmediate,则使用 setImmediate 函数来触发 flushCallbacks 函数
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
 // 如果以上都不支持,则回退到使用 setTimeout 来触发 flushCallbacks 函数。
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

可以看到Vue2的nextTick的实现借助了promiseMutationObserver,setImmediate, setTimeout

MutationObserver: 用于监控DOM(文档对象模型)的变化。当观察的DOM元素或其子元素发生变化时,它会触发一个回调函数

setImmediate: 是一个用于在 Node.js 中执行异步操作的函数。它类似于 setTimeout,但是会在当前事件循环的末尾立即执行回调函数,而不是等待一定的延迟时间

最后,定义了 nextTick 函数,用于添加回调函数到 callbacks 数组中

function nextTick(cd, ctx) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e: any) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  // 是否有待执行的任务来决定是否调用 timerFunc 来触发执行
  if (!pending) {
  	// 如果存在回调函数,则将 pending 置为 true,然后调用 timerFunc。
    pending = true
    timerFunc()
  }
	// 如果不传入回调函数,则返回一个 Promise 对象,在回调函数执行完成后,Promise 被 resolve。
   if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

二、Vue3 的nextTick作用和源码解读

官网解释:

当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。

vue3中简化了实现并保持了一致性,弃用了MutationObserver,setImmediate, setTimeout,专门采用了Promise来实现nextTick,减少了代码的复杂性,保证在大多数现代浏览器中可正常使用

源码

// runtime-core/scheduler.ts

// 创建了一个已解决的 Promise。通常用作基础 Promise,以便在其上链式调用异步操作
const resolvedPromise = Promise.resolve()

// 声明了一个变量 currentFlushPromise,初始值为 null。
// 这个变量用于追踪当前的 Promise 状态
let currentFlushPromise = null

function nextTick(this,fn) {
  const p = currentFlushPromise || resolvedPromise
  return fn ? p.then(this ? fn.bind(this) : fn) : p
}

currentFlushPromise的作用:控制和管理 nextTick 函数中回调函数的执行时机,以及确保回调函数按照正确的顺序执行。

currentFlushPromise 运行方式后续补充。。。。

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
nextTickVue的一个API,用于在下一个DOM更新循环结束之后执行延迟回调。它的源码如下: ```js export function nextTick (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 if (useMacroTask) { macroTimerFunc() } else { microTimerFunc() } } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } } ``` 这里简单解释一下它的实现: 1. 定义了一个callbacks数组,用于存放需要执行的回调函数。 2. 将回调函数推入callbacks数组。 3. 如果pending为false,则设置pending为true,并且调用macroTimerFunc或microTimerFunc函数(根据useMacroTask的值来决定调用哪个函数)。 4. 如果cb不存在且Promise存在,则返回一个Promise实例,同时将_resolve函数赋值为resolve函数。 5. 如果cb存在,则执行cb函数,如果发生异常,则调用handleError函数处理异常。 其,macroTimerFunc和microTimerFunc函数的作用是将flushCallbacks函数推入任务队列,在下一个事件循环执行flushCallbacks函数。 至于useMacroTask的值,则根据浏览器的支持情况来决定。在一些浏览器使用setImmediate会比使用setTimeout性能更好,因此优先使用setImmediate。否则,使用MessageChannel或setTimeout。 至此,我们对nextTick源码进行了简单的讲解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值