思维导图
前言
上一节我们实现了当我们改变了属性值之后页面会自动触发更新的流程,本节我们要考虑的是,当我们多次修改同一个属性的时候避免渲染多次的情况发生,所以我们在内部采用了异步更新页面的流程。
正文
举例
var vm = new Vue({
el: '#app',
data: {
name: '小明',
age: 20
}
})
vm.name = '天明'
vm.name = '天明1'
vm.name = '天明2'
vm.name = '天明3'
watcher.js
update() {
//会多次更新
this.get()
}
我们可以看见这个时候重新渲染页面的函数被执行了四次,vue在实现的时候为了考虑页面的性能,就在内部做了一定的优化。下面让我们一起来看看吧。
批量异步渲染
watcher.js
class Watcher {
//...
update() {
//会多次更新
queueWatcher(this)
}
run() {
console.log('update')
this.get()
}
}
+scheduler.js
import { nextTick } from "../utils"
let queue = [] //存储要更新的watcher
let has = {} //检测有没有重复的watcher
let pending = false
function flushSchedulerQueue() {
queue.forEach(watcher => watcher.run())
queue = []
has = {}
pending = false
}
export function queueWatcher(watcher) {
let id = watcher.id
if (has[id] === undefined) {
has[id] = true
queue.push(watcher)
if (!pending) {
nextTick(flushSchedulerQueue)
pending = true
}
}
}
$nextTick
init.js
Vue.prototype.$nextTick = nextTick
vm.$nextTink与异步更新在一个微任务中
util.js
// 之前是每一个异步更新都开启一个promise微任务,现在要优化成多个异步更新只开启一个微任务
let callbacks = []
let waiting = false
//批量执行,同步代码都执行完毕之后才会执行。
function flushCallbacks() {
callbacks.forEach(fn => fn())
callbacks = []
waiting = false
}
export function nextTick(fn) {
callbacks.push(fn)
if(!waiting) {
Promise.resolve().then(flushCallbacks)
waiting = true
}
}
//多个nextTick()是一次 promise的then()
小结
本节主要是对象属性改变之后的异步更新处理,下一节主要写数组的依赖收集。