前言
- 为什么异步更新,拿data中的属性举例,如果某个数据多次的去赋值,每次都去重新编译、比较vnode、渲染Dom 耗费性能
- 我们把更新视图的数据行为收集起来,去重,防抖 通过异步行为实现(如setTimeout, Ajax...)
- 本次拿data举例
html 和 Javascript模板
<div id="app">{{ name }}</div><script>var vm = new Vue({data: {name: 'one'}
})
vm.$mount('#app')setTimeout(() => {
vm.name = 'two'vm.name = 'three'vm.name = 'four'vm.name = 'five'vm.$nextTick(()=>{ // 只执行一次console.log(vm.$el)
})
}, 1000)复制代码
正题
Vue.nextTick方法(重点)
import { nextTick } from './utils'Vue.prototype.$nextTick = nextTick复制代码
/** nextTick *//**
* @description callbacks 队列要执行的异步函数
* @description waiting 防抖
*/let callbacks = []let waiting = false/**
* @description 执行回调函数
*/function flushCallbacks() {
callbacks.forEach(cb => cb())
waiting = falsecallbacks = []
}/**
* @description 异步函数的降级处理(兼容适配)
*/function timer(flushCallbacks) {let timerFn = () => {}if (Promise) {
timerFn = () => { Promise.resolve().then(flushCallbacks) }
} else if (MutationObserver) {let textNode = document.createTextNode(1)let observe = new MutationObserver(flushCallbacks)
observe.observe(textNode, { characterData: true })
timerFn = () => { textNode.textContent = 3 }
} else if (setImmediate) {
timerFn = () => { setImmediate(flushCallbacks) }
} else {
timerFn = () => { setTimeout(flushCallbacks) }
}
timerFn()
}export function nextTick(cb) {
callbacks.push(cb)if (!waiting) {
timer(flushCallbacks)
waiting = true}
}复制代码
scheduler Watcher里update中调度的方法
import { nextTick } from '../utils'/**
* @description queue 存放watcher
* @description has 存放那些watcher
* @description pending 防抖
*/let queue = []let has = {}let pending = false/** 核心方法 *//**
* @description 执行watcher
*/function flushSchedulerQueue() {for (let i = 0; i < queue.length; i++) {
queue[i].run()
}
queue = []
has = {}
pending = false}/**
* @description 去除重复的watcher
* @description 有多个watcher时 进行批处理(防抖)
* @description 当前执行栈中代码执行完毕后,会先清空微任务,在清空宏任务 希望更早的渲染页面 nextTick
*/export function queueWatcher(watcher) {const id = watcher.idif (has[id] == null) {
queue.push(watcher)
has[id] = trueif (!pending) {
nextTick(flushSchedulerQueue)
pending = true}
}
}复制代码
new Wacther文件
import { popTarget, pushTarget } from './dep'
import { queueWatcher } from './scheduler'
let id = 0
class Watcher {
constructor(vm,exprOrFn,cb,options){
this.vm = vm
this.exprOrFn = exprOrFn
this.cb = cb
this.options = options
this.id = id++
// 视图更新 就是上面的updateComponent方法
this.getter = exprOrFn
this.deps = []
this.depsId = new Set()
this.get()
}
get(){
pushTarget(this)
this.getter()
popTarget()
}
update(){
// 看这里 异步更新操作 就是将更新缓存起来(做一些去重, 防抖)然后一起调用,最后还是调用下方run方法
queueWatcher(this)
}
run(){
this.get()
}
addDep(dep){
let id = dep.id;
if(!this.depsId.has(id)){
this.depsId.add(id);
this.deps.push(dep);
dep.addSub(this)
}
}
}
export default Watcher复制代码
完