源代码级答案 "来自大公司的高频 Vue 面试问题(上) - 为什么 Vue 使用异步渲染?
最编程
2024-03-13 13:22:58
...
我们先来想一个问题:如果Vue
不采用异步更新,那么每次数据更新时是不是都会对当前组件进行重写渲染呢?
答案是肯定的,为了性能考虑,会在本轮数据更新后,再去异步更新视图。
通过一张图来说明Vue
异步更新的流程:
- 第一步调用
dep.notify()
通知watcher
进行更新操作。对应源码src/core/observer/dep.js
中的 37 行。
notify () { // 通知依赖更新
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // 依赖中的update方法
}
}
- 第二步其实就是在第一步的
notify
方法中,遍历subs
,执行subs[i].update()
方法,也就是依次调用watcher
的update
方法。对应源码src/core/observer/watcher.js
的 164 行
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update () {
/* istanbul ignore else */
if (this.lazy) { // 计算属性
this.dirty = true
} else if (this.sync) { // 同步watcher
this.run()
} else {
queueWatcher(this) // 当数据发生变化时会将watcher放到一个队列中批量更新
}
}
- 第三步是执行
update
函数中的queueWatcher
方法。对应源码src/core/observer/scheduler.js
的 164 行。
/**
* Push a watcher into the watcher queue.
* Jobs with duplicate IDs will be skipped unless it's
* pushed when the queue is being flushed.
*/
export function queueWatcher (watcher: Watcher) {
const id = watcher.id // 过滤watcher,多个属性可能会依赖同一个watcher
if (has[id] == null) {
has[id] = true
if (!flushing) {
queue.push(watcher) // 将watcher放到队列中
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next immediately.
let 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 (process.env.NODE_ENV !== 'production' && !config.async) {
flushSchedulerQueue()
return
}
nextTick(flushSchedulerQueue) // 调用nextTick方法,在下一个tick中刷新watcher队列
}
}
}
- 第四步就是执行
nextTick(flushSchedulerQueue)
方法,在下一个tick
中刷新watcher
队列