缘起
群友在群里问了下nextTick和updated的执行时间。我一开始觉得肯定是先updated再nextTick。然而实验结果稍微有点意外,要看注册nextTick函数的时间。在数据变动之前就比updated钩子先,之后则后。
原理
vue为了减少数据多次变动导致的watcher多次被过量更新,实际上处理数据变动产生的副作用是放在异步的timerFunc函数里操作的,劫持timerFunc为timeout并手动设置时间就会发现dom更新的时间被对应的推迟了。关于timerFunc函数的机制可以去翻源码,也很有趣。而vue在内部实际上依然是通过nextTick来触发timerFunc的,在源码中搜索timerFunc()发现只在nextTick中出现了一次。通过检索源代码可以发现,vue执行updated钩子的方法是flushSchedulerQueue。
function queueWatcher (watcher) {
var id = watcher.id;
if (has[id] == null) {
has[id] = true;
if (!flushing) {
queue.push(watcher);
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next immediately.
var i = queue.length - 1;
while (i > index && queue[i].id > watcher.id) {
i--;
}
queue.splice(i + 1, 0, watcher);
}
// queue the flush
if (!waiting) {
waiting = true;
if (!config.async) {
flushSchedulerQueue();
return
}
nextTick(flushSchedulerQueue);
}
}
}
调用flushSchedulerQueue有两种方式,但是根据config.async的注释,同步模式只给测试工具用。
/**
* Perform updates asynchronously. Intended to be used by Vue Test Utils
* This will significantly reduce performance if set to false.
*/
正好就对上了,updated钩子也是通过注册在nextTick相关的callback函数们里执行的。所以nextTick注册的函数跟updated钩子的执行顺序和注册在数据变动之前还是之后有关。
总结
- vue处理数据变动产生的副作用是放在异步的timerFunc函数里操作的
- nextTick注册的函数和updated钩子的执行顺序和注册顺序有关
- 想要通过nextTick拿到数据变更后的dom建议在数据变更后再注册。