上一篇有提到事件循环,及完整的事件循环(Event loop)过程解析,趁热打铁,理解一下vue中的nextTick()。
function nextTick(callback?: () => void): Promise<void>
官方文档中的解释是:“在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用nextTick(),获取更新后的 DOM。”
简单来说,nextTick(),是将回调函数延迟执行,在下一次DOM更新数据后调用,当数据更新并在DOM中渲染后,自动执行该函数。
看下官方示例:
<script>
import { nextTick } from 'vue'
export default {
data() {
return {
count: 0
}
},
methods: {
async increment() {
this.count++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
}
}
</script>
<template>
<button id="counter" @click="increment">{
{ count }}</button>
</template>
nextTick源码理解
简单理解一下之后,我们看下源码吧,在src/core/instance/render.js中 将nextTick定义到vue原型链上,这里的 this是指当前组件的this。
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
在src/core/util/next-tick.js中,通过export暴露出了 nextTick 函数。
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()
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
我们可以看到,nextTick接收两个参数,第一个是函数cb(即 我们要延迟执行的函数),第二个是this上下文。
在函数体内,callbacks是一个数组,用来存储所有需要执行的回调函数。判断cb存在,就把cb存到callbacks数组中,同时把cb的上下文指向组件的this。cb不存在,就把_resolve函数放到callbacks数组中。
然后判断pending的值,pending用来控制状态,判断是否有正在执行的回调函数。当pending为fa