- 上一节完成了组件的自更新,但是一旦组件自身的响应式数据发生改变,组件就要自动重新执行渲染函数,这样做有一个缺陷。
const VueComponent = {
props:{
a:Number,
b:{}
},
data(){
return {name:'zf',a:100}
},
render(){
console.log(this.a)
return h('button',{onClick:()=>{
this.a++
this.a++
this.a++
}}, h('span',this.a))
}
}
render(h(VueComponent),app);
- 当点击按钮时,数据a会改变三次,render函数也会执行三回,这样做效率很低,因为只需要等a全部改变完,执行一次渲染函数即可。
- 这里可以利用响应系统的调度执行去改善。
- ReactiveEffect类可以传入第一个函数为渲染函数,当数据改变去执行这个函数重新渲染页面,还可以传入一个包含调度函数的对象,如果这个函数存在,则不执行渲染函数,改为执行这个函数,这个函数的默认参数依然为渲染函数。
- 调度函数中每当数据发生改变,调用调度函数,调度函数内部有一个set数组,存储每次传入的渲染函数job
- 开启一个异步任务,第一次执行调度函数时,在异步任务中依次执行job,并清空数组
- 设置一个守卫,在异步任务没执行完之前,再次开启改调度函数无效
const queue = new Set()
let isFlushing = false
const resolvePromise = Promise.resolve()
export function queueJob(job){
queue.add(job)
if(!isFlushing){
isFlushing = true
resolvePromise.then(()=>{
try{
queue.forEach(job =>job())
}finally{
isFlushing = false
queue.clear()
}
})
}
}
const effect = new ReactiveEffect(componentUpdate,()=>{queueJob(instance.update)})
let update = instance.update = effect.run.bind(effect)
update()
- 自此,在第一次a++时,开启调度函数,后面a++,调度函数将更新函数追加到调用栈中,set会自动去重,因此只有一个更新函数在调用栈,由于第一次的守卫阻挡,后面两次a++执行调度函数不会执行异步任务。第一次异步任务执行完,页面渲染。