前言

  • 为什么异步更新,拿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)复制代码

vue2核心原理(简易)-异步更新(Vue.nextTick)笔记_vue2

正题

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复制代码