目录
异步渲染队列
异步更新原理
异步更新队列(对模板渲染的性能优化)
当模板中存在多处变量依赖的时候,每一个变量修改的时候,都会导致一次渲染,是否可以优化?
let active; // 表明当前是一个正在执行的函数
let watch = function(cb) { // 接收一个回调函数参数
active = cb;
active();
active = null; // 保证active只会添加一次,用完销毁掉
}
let queue = []; // 声明对列
let nextTick = (cb) => Promise.resolve().then(cb); // 将队列放置入微任务队列
let queueJob = job => {
if(!queue.includes(job)){// 如果queue队列里没有job
// 就把他加进去
queue.push(job);
nextTick(flushJobs);
}
}
let flushJobs = () => {
let job;
while((job = queue.shift()) !== undefined) {
// shift() 方法移除数组的第一项。
// shift() 方法会改变数组的长度。
// shift 方法的返回值是被移除的项目。
// shift() 方法会改变原始数组。
// 如需删除数组的最后一项,请使用 pop() 方法。
// 执行任务
job();
}
}
// 假如说有很多 要监听的数据,和它的回调执行,就要有一个依赖收集类
class Dep { // 依赖收集类
deps = new Set();
depend() { // 进行一次依赖收集,相应的依赖加到deps里面
if(active){ // active非空的
this.deps.add(active);
}
}
notify() { // 对当前有的deps方法,进行一次执行
this.deps.forEach(dep => queueJob(dep)) // 添加队列
}
}
let ref = initValue => {
// 闭包2个对象,一个是value,一个是依赖Dep
let value = initValue; // 闭包一个value,初始值是initValue
let dep = new Dep(); // 初始化Dep
// 新对象,里面包含了一个value属性,这个属性里面包括了get,set函数
return Object.defineProperty({}, "value",{ // 代理
get() {
dep.depend(); // 添加依赖
return value;
},
set(newValue) {
value = newValue;
// active(); 执行
dep.notify()
}
})
}
// 因为我们要使用Object.defineProperty,把x修改成引用类型的值
// x使用ref(1)时,就创建了个新的对象【ref(1),得到了一个代理的对象】
let x = ref(1); //
let y = ref(2); //
let z = ref(3); //
let str;
watch(() => {
str = `hello${x.value}- ${y.value} -${z.value}`
console.log(1,str);
document.write(`${str}<br/>`);
})
x.value = 2;
y.value = 2;
z.value = 2;
x.value = 3;
nextTick(() => {
console.log(x.value);
console.log(2, str);
console.log(x.value);
})
nextTick
在下次DOM更新循环结束之后执行延迟回调(把dom渲染队列【通过Promise】放入了微任务队列)
Vue.nextTick([callback, context]) // 全局的渲染,this为context
vm.$nextTick([callback]) // 实例vm上的,this会绑定在vm实例上
// 它们都会将回调延迟到下次DOM更新循环后执行。
// 通常用于在修改数据之后使用这个方法,在回调中获取更新后的DOM。
mounted: function() { // 视图渲染完毕
this.$nextTick(function() {
// 这里代码将在当前组件和所有子组件挂载完毕之后执行
})
}