Vue2 nextTick 解析
会执行函数生成一个闭包函数
export const nextTick = (function () {...})()
// 函数体
const callbacks = [] // 所有的$nextTick(fn) ,这里的fn 都会 push 到这个数组中
let pending = false // 加锁
let timerFunc // 异步处理方法
// 处理 callbask 里面所有回调方法
function nextTickHandler () {
pending = false // 解锁
const copies = callbacks.slice(0) // 转换数组
callbacks.length = 0 //
for (let i = 0; i < copies.length; i++) {
copies[i]() // 执行回调函数
}
}
// 判断浏览器是否原生支持Promise (使用微任务Jobs 处理比宏任务Task,可以更早的时机执行 )
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve() // 生成一个空的Promise 实例
// 封装一个函数
timerFunc = () => {
// 处理nextTick 接受的所有回调函数
p.then(nextTickHandler)
}
}
// 判断浏览器环境是否支持 MutationObserver (H5新特性)
else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
var counter = 1
var observer = new MutationObserver(nextTickHandler) // 创建监听对象MutationObserver
var textNode = document.createTextNode(String(counter)) // 创建一个文本dom节点
observer.observe(textNode, { // 监听这个文本 节点
characterData: true
})
// 封装一个函数
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter) // 通过修改文本节点的值,从而触发MutationObserver 监听的回调 nextTickHandler
}
}
// 以上都不支持的话, 就使用宏任务(Task) 定时器实现
else {
// fallback to setTimeout
/* istanbul ignore next */
timerFunc = setTimeout
}
// 主要的方法 ;闭包函数返回的函数, this.$nextTick 调用的就是这里的方法
return function queueNextTick (cb: Function, ctx?: Object) {
// cb: nextTick 回调函数
// ctx: vue的实例
const func = ctx // 如果有ctx, 就call指向当前回调函数的this 为 ctx
? function () { cb.call(ctx) }
: cb
// 将回调函数push 到 callbacks 中
callbacks.push(func)
// 通过pending判断锁, 是否执行完所有的回调了, 或者有可能是第一次进入
if (!pending) {
pending = true // 加锁
timerFunc(nextTickHandler, 0) // 通常写timerFunc() 就行, 这是为了兼容setTimeout
}
}
评论区