场景和用法
场景:数据变化之后想要获取更新之后的最新的dom
- 因为数据变化之后,组件的更新watcher 并不是同步执行的,而是通过nextTick放到异步队列里面等待执行【同步代码走完之后才会执行】
- 数据发生变化之后,组件更新的watcher也是通过nextTick进行异步更新
原理
- 数据发生变化之后,触发当前数据的set方法【初始化的时候已经进行get和set的劫持了】
- set方法内部会执行dep.notify【通知收集到的依赖进行更新】
- notify方法内部,会遍历subs【收集到的依赖数组】,进行更新【watcher.update】
- watcher.update:调用queueWatcher(wacher)【当前更新的watcher实例】
- queueWatcher方法:获取当前watcherid【唯一值】
-
- 判断当前wathcer是否已经被添加,对 会wathcer 进行去重。
- 如果没有添加到队列里面 >> 进行添加 >> queue里面push进去当前组件的更新watcher
- 调用nextTick方法,把flushSchedulerQueue【可以更新queue里面的所有的watcher,调用的是watcher的run方法 >> run方法的作用,就行执行页面的更新】
nextTick(flushSchedulerQueue)
function flushSchedulerQueue() {
for (let i = 0; i < queue.length; i++) {
queue[i].run(); // 可以更新页面
}
queue = []; // [watcher1, watcher2, watcher3, ....]
has = {};
}
nextTick的实现
===> nextTick执行传入的回调的时候不是同步执行,而是异步执行
function nextTick(fn) {
callback.push(fn)
}
- 往callbacks里面push进去当前flushSchedulerQueue
- 全局会定义一个pending标识,分批更新
- 如果这个标识为false,证明目前没有更新的队列 >> 可以执行更新
- 异步任务执行的时候,根据当前的宿主环境判断异步任务类型【promise的优先级最高】===========> Promise/mutationObserver/setImidate/setTimeout
- 同步代码走完之后,开始执行异步队列里面存储进去的异步任务,实现更新
代码举例
<template>
<div id="app">
{{ msg }} {{ age }}
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: 'msg',
age: 10
}
},
mounted() {
console.log(this)
},
methods: {
show() {
this.age = 20
this.$nextTick(() => {
console.log(document.querySelector('#app').innerHTML); // newMsg
})
this.msg = 'newMsg';
}
}
}
</script>