Vue3 nextTick 源码分析
vue版本
3.2.37
前言
在之前的Vue2分析中,提到了Vue2的nextTick是维护了一个callbacks数组,每次更新过程中只插入一个微任务,执行放在callbacks数组中的回调。
而Vue3不同,Vue3的nextTick和Promise基本没什么区别,set过程的更新看似也不再依赖,nextTick进行。仅仅只是将创建一个resovled状态的Promise,将传入的函数放入回调中罢了。以下是vue3的nextTick源码:
const resolvedPromise = /*#__PURE__*/ Promise.resolve();
let currentFlushPromise = null;
function nextTick(fn) {
const p = currentFlushPromise || resolvedPromise;
return fn ? p.then(this ? fn.bind(this) : fn) : p;
}
正因为如此,同样的代码,在Vue2和Vue3中会有不同的表现。参考以下代码:
Promise.resolve().then(()=>{
console.log('开始的Promise回调')
})
this.$nextTick(()=>{
console.log('第一次nextTick的回调')
})
Promise.resolve().then(()=>{
console.log('修改数据之前的Promise回调')
})
this.name = 'kirito' // 这里进行赋值操作
Promise.resolve().then(()=>{
console.log('修改数据之后的Promise回调')
})
this.$nextTick(()=>{
console.log('最后的nextTick的回调')
})
以上代码的运行结果,在Vue2中是:
开始的Promise回调
第一次nextTick的回调
最后的nextTick的回调
修改数据之前的Promise回调
修改数据之后的Promise回调
只要调用了nextTick或者对数据进行了变更,那么放在之后的Promise回调,一定是排在后面执行的。
而同样的代码:
const name = ref('yuuki')
const test2 = () => {
Promise.resolve().then(()=>{
console.log('开始的Promise回调')
})
nextTick(()=>{
console.log('第一次nextTick的回调')
})
Promise.resolve().then(()=>{
console.log('修改数据之前的Promise回调')
})
name.value = 'kirito' // 这里进行赋值操作
Promise.resolve().then(()=>{
console.log('修改数据之后的Promise回调')
})
nextTick(()=>{
console.log('最后的nextTick的回调')
})
}
在Vue3中的执行结果是:
开始的Promise回调
第一次nextTick的回调
修改数据之前的Promise回调
修改数据之后的Promise回调
最后的nextTick的回调
看上去完全是按照Promise加入微任务队列的逻辑,一次nextTick就是插入一个微任务队列,不维护callbacks数组。
实例分析
上述示例代码,赋值操作如果稍稍改变一下位置,又会与预想的输出截然不同:
const name = ref('yuuki')
const age = ref(18)
const test2 = () => {
Promise.resolve().then(()=>{
console.log('开始的Promise回调')
})
name.value = 'kirito' // 这里进行赋值操作
nextTick(()=>{
console.log('第一次nextTick的回调')
})
Promise.resolve().then(()=>{
console.log('修改数据之前的Promise回调')
})
Promise.resolve().then(()=>{
console.log('修改数据之后的Promise回调')
})
nextTick(()=>{
console.log('最后的nextTick的回调')
})
}
以上的代码,执行了之后输出变成:
开始的Promise回调
修改数据之前