核心源码位置
Vue 2 的 nextTick
实现主要在 src/core/util/next-tick.js
文件中。
完整源码结构
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
export let isUsingMicroTask = false
const callbacks = []
let pending = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
let timerFunc
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
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
timerFunc()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
关键点解析
1. 回调队列管理
callbacks
: 存储所有待执行的回调函数pending
: 标记是否已经有回调队列正在等待执行flushCallbacks
: 执行所有回调并清空队列
2. 异步执行策略 (timerFunc)
Vue 2 采用了降级策略来选择最佳的异步执行方式,优先级如下:
(1) 首选 Promise (微任务)
(2) 次选 MutationObserver (微任务)
(3) 再次选 setImmediate (宏任务)
(4) 最后选择 setTimeout (宏任务)
3. nextTick 函数实现
支持两种调用方式:
回调函数形式:
nextTick(callback)
Promise 形式:
nextTick().then(...)
错误处理:捕获回调执行中的错误并通过 Vue 的错误处理系统处理
与 Vue 3 的主要区别
兼容性处理:Vue 2 有复杂的降级策略,Vue 3 只使用 Promise
微任务控制:Vue 2 明确标记
isUsingMicroTask
来跟踪是否使用微任务实现复杂度:Vue 2 需要处理更多边界情况和浏览器怪癖
Promise 返回:Vue 2 需要额外处理 Promise 返回的逻辑
设计思想
批量更新:通过回调队列实现多个更新的批量处理
异步更新:确保 DOM 更新是异步执行的,提高性能
执行时机:尽可能使用微任务以更早执行回调
兼容性:在各种环境中都能正常工作
Vue 2 的 nextTick
实现展示了框架如何在不同浏览器环境中提供一致的异步行为,同时也体现了对性能的极致追求。