学好Vue的基础--Vue源码解析(二)

nextTick

注:本系列文章是对Vue源码学习的一个总结笔记,如有侵犯您的隐私请联系我修改或者删除。 参考
书籍:vue权威指南
本文笔记整理以及原理参考博客:Vue $nextTick 原理、vue nextTick深入理解-vue性能优化、DOM更新时机、事件循环机制、Vue.nextTick 的原理和用途。

  1. nextTick的由来:
    vue实现响应式并不是数据发生变化之后DOM立即变化,而是等同一事件循环中所有的数据变化完成后,再统一更新。这也是Vue的一个重要的概念:异步更新队列(JS运行机制 、 事件循环)。
  2. nextTick的使用场景:
    在数据获取后,需要对新视图进行下一步操作或者其他操作的时候,发现获取不到DOM元素。

源码解析

  • 定义变量

        const callbacks = [];//缓存数组函数
        let pending = false;//是否在执行
        let timerFunc;//保存要执行的函数
    
  • 创建$nextTick内实际调用的函数

        function flushCallbacks () {
           
          pending = false//是否在执行-->否
          const copies = callbacks.slice(0)//复制函数的数组副本
          callbacks.length = 0//函数数组清空
          for (let i = 0; i < copies.length; i++) {
           
            copies[i]()
          }//依次执行函数
        }
    
  • Vue 会根据当前浏览器环境是否支持使用原生的 Promise.thenMutationObserver(实质是一个可以监听DOM变化的接口),如果都不支持,就会采用 setTimeout 代替,目的是 延迟函数到 DOM 更新后再使用。

    promise.then的延时调用
        if (typeof Promise !== 'undefined' && isNative(Promise)) {
           
          const p = Promise.resolve()
          timerFunc = () => {
           
            p.then(flushCallbacks)
            if (isIOS) setTimeout(noop)
          }
          isUsingMicroTask = true
        } 
    

    在浏览器支Promise的情况下,Promise.then方法可以将函数延迟到当前函数调用栈最末端,也就是最后调用该函数。从而做到延迟。

    MutationObserver的延时调用
        else if (!isIE && typeof MutationObserver !== 'undefined' && (
          isNative(MutationObserver) ||
          MutationObserver.toString() === '[object MutationObserverConstructor]'
        )) {
           
          let counter = 1
          const observer = new MutationObserver(flushCallbacks)
          const textNode = document.createTextNode(String(counter))
          observer.observe(textNode, {
           
            characterData: true
          })
          timerFunc = () => {
           
            counter = (counter + 1) % 2
            textNode.data = String(counter)
          }
          isUsingMicroTask = true
        } 
    

    MutationObserver功能是监听dom节点的变动,在所有dom变动完成后,执行回调函数。
    具体有一下几点变动的监听:
    childList:子元素的变动
    attributes:属性的变动
    characterData:节点内容或节点文本的变动
    subtree:所有下属节点(包括子节点和子节点的子节点)的变动
    这样我们就可以使用MutationObserver监听dom节点变化,在dom节点变化完成后再执行函数。

    setTimeOut的延时调用
        else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
           
          timerFunc = () => {
           
            setImmediate(flushCallbacks)
          }
        } else {
           
          timerFunc = () => {
           
            setTimeout(flushCallbacks, 0)
          }
        }
    

    setTimeout(func,0)会将func函数延迟到下一次回调函数的开始,也就是当前函数执行完毕后再执行该函数。

  • 闭包函数
           //nextTick接收的函数,参数1:回调函数 参数2:回调函数的执行上下文
            return function queueNextTick(cb,ctx) {
           
                //用于接收触发Promise.then中回调的函数
                //向回调函数中pushcallback
                var _resolve;
                callbacks.push(function () {
           
                    //如果有回调函数,执行回调函数
                    if(cb) {
           cb.call(ctx);}
                    //触发Promise的then回调
                    if(_resolve) {
           _resolve(ctx);}
                });
                //是否执行刷新callback队列
                if(!pending){
           
                    pending=true;
                    timerFunc();
                }
                //如果没有传递回调函数,并且当前浏览器支持promise,使用promise实现
                if(!cb && typeof  Promise !=='undefined'){
           
                    return new Promise(function (resolve) {
           
                        _resolve=resolve;
                    })
                }
            }
    

你可能感兴趣的:(vue,前端框架,javascript,vue)