前言:
因为不采用异步更新,在每次更新数据都会对当前组件进行重新渲染。所以为了性能考虑,vue 会在本轮数据更新后,再去异步更新视图。
vue是组件级更新,当前组件里的数据变了,它就会去更新这个组件。当数据更改一次组件就要重新渲染一次,性能不高,为了防止数据一更新就更新组件,所以做了个异步更新渲染。(核心的方法就是nextTick)
原理:
当数据变化后会调用notify方法,将watcher遍历,调用update方法通知watcher进行更新,这时候watcher并不会立即去执行,在update中会调用queueWatcher方法将watcher放到了一个队列里,在queueWatcher会根据watcher的进行去重,多个属性依赖一个watcher,如果队列中没有该watcher就会将该watcher添加到队列中,然后通过nextTick异步执行flushSchedulerQueue方法刷新watcher队列。flushSchedulerQueue中开始会触发一个before的方法,其实就是beforeUpdate,然后watcher.run() 才开始真正执行watcher,执行完页面就渲染完成啦,更新完成后会调用updated钩子。
源码:
1.notify
notify () { // 通知存储的依赖更新
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
subs.sort((a, b) => a.id - b.id)
}
// 循环watcher,发布订阅模式
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // 调用watcher中的update方法
}
}
dep中存储的就是watcher,每个属性一个dep
2.update
update () {
if (this.lazy) { // 计算属性
this.dirty = true
} else if (this.sync) { // 同步watcher
this.run()
} else {
queueWatcher(this) // 将watcher放入队列,this就是当前的watcher
}
}
3.queueWatcher
export function queueWatcher (watcher: Watcher) {
const id = watcher.id // 过滤watcher,每个watcher有一个id,多个属性依赖同一个watcher
if (has[id] == null) { // 如果没有就会添加进去
has[id] = true
if (!flushing) {
queue.push(watcher) // 并且将watcher放到队列中去
} else {
let i = queue.length - 1
while (i > index && queue[i].id > watcher.id) {
i--
}
queue.splice(i + 1, 0, watcher)
}
if (!waiting) {
waiting = true
if (process.env.NODE_ENV !== 'production' && !config.async) {
flushSchedulerQueue() // 会做一个清空queue的操作
return
}
nextTick(flushSchedulerQueue) // 在下一个tick中刷新watcher队列
}
}
}
4.flushSchedulerQueue
function flushSchedulerQueue () {
currentFlushTimestamp = getNow()
flushing = true
let watcher, id
queue.sort((a, b) => a.id - b.id)
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
if (watcher.before) {
watcher.before() // 触发一个before方法
}
id = watcher.id
has[id] = null
watcher.run() // 开始执行watcher,执行完页面就渲染完成啦
if (process.env.NODE_ENV !== 'production' && has[id] != null) {
circular[id] = (circular[id] || 0) + 1
if (circular[id] > MAX_UPDATE_COUNT) {
warn(
'You may have an infinite update loop ' + (
watcher.user
? `in watcher with expression "${watcher.expression}"`
: `in a component render function.`
),
watcher.vm
)
break
}
}
}
const activatedQueue = activatedChildren.slice()
const updatedQueue = queue.slice()
resetSchedulerState()
callActivatedHooks(activatedQueue)
callUpdatedHooks(updatedQueue) // 更新完成后会调用updated钩子
if (devtools && config.devtools) {
devtools.emit('flush')
}
}