新闻资讯
关于Vue暴露的全局nextTick
关于Vue暴露的全局nextTick
继续来看下面的这段代码:
<div id="example">
<div ref="test">{{test}}</div>
<button @click="handleClick">tet</button>
</div>
var vm = new Vue({
el: '#example',
data: { test: 'begin',
},
methods: { handleClick() {
this.test = 'end';
console.log('1') setTimeout(() => { // macroTask
console.log('3')
}, 0);
Promise.resolve().then(function() { //microTask
console.log('promise!')
})
this.$nextTick(function () {
console.log('2')
})
}
}
})
在Chrome下,这段代码执行的顺序的1、2、promise、3。
可能有同学会以为这是1、promise、2、3,其实是忽略了一个标志位pending。
我们回到nextTick函数return的queueNextTick可以发现:
return function queueNextTick (cb?: Function, ctx?: Object) { let _resolve
/*cb存到callbacks中*/
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, reject) => {
_resolve = resolve
})
}
}
这里面通过对pending的判断来检测是否已经有timerFunc这个函数在事件循环的任务队列等待被执行。如果存在的话,那么是不会再重复执行的。
最后异步执行nextTickHandler时又会把pending置为false。
function nextTickHandler () {
pending = false /*执行所有callback*/
const copies = callbacks.slice(0)
callbacks.length = 0 for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
所以回到我们的例子:
handleClick() {
this.test = 'end';
console.log('1') setTimeout(() => { // macroTask
console.log('3')
}, 0);
Promise.resolve().then(function() { //microTask
console.log('promise!')
});
this.$nextTick(function () {
console.log('2')
});
}
代码中,this.test = 'end'必然会触发watcher进行视图的重新渲染,而我们在文章的Watcher一节中就已经有提到会调用nextTick函数,一开始pending变量肯定就是false,因此它会被修改为true并且执行timerFunc。之后执行this.nextTick(fn)只是把传入的fn置入callbacks之中。此时的callbacks有两个function成员,一个是flushSchedulerQueue,另外一个就是this.$nextTick()的回调。
因此,上面这段代码中,在Chrome下,有一个macroTask和两个microTask。一个macroTask就是setTimeout,两个microTask:分别是Vue的timerFunc(其中先后执行flushSchedulerQueue和function() {console.log('2')})、代码中的Promise.resolve().then()。
原文链接:https://juejin.im/post/5e53f07d51882549036940fc
回复列表