vue2源码-九、异步更新

异步更新

  1. 异步更新原因

    以下情况下:

    vm.name = '123'
    vm.name = '234'
    vm.name = '123'
    ...
    

    如果我们频繁的修改一个数据,就会多次触发视图渲染dep.notify->watcher.update

    这样就会降低性能,因此就需要采用异步更新策略,仅仅在最后执行一次视图更新操作。

  2. 思路

    当数据变化时,先将数据变更的逻辑缓存下来,不直接处理,如果有相同的数据更新进行合并,在最后仅执行一次视图更新。

  3. 缓存watcher更新逻辑

    思路:将watcher集中缓存到一个队列中,在缓存过程中进行合并,最后一次性执行。

    queueWatcher方法:缓存队列,用于watcher的去重和缓存(唯一标识id)

    let queue = []; // 用于缓存渲染watcher
    let has = []; // 存放watcher唯一标识(id),用于watcher的查重
    let pending = false; // 防抖 等待状态标识,用于控制setTimeout直走一次
    function queueWatcher(watcher) {
      const id = watcher.id; // 获取watcher的id
      if (!has[id]) { // has对象没有当前watcher
        queue.push(watcher); // 缓存watcher。但不调用
        has[id] = true; // 缓存标记
    
        if (!pending) { // 不需要等待,相当于防抖策略,只执行一次
          nextTick(flushSchedulerQueue()); // 执行函数(宏任务)
        }
    
        pending = true; // 首次进入被置为true,使微任务执行完成宏任务执行
      }
    }
    

    解释一下pending:

    • queueWatcher方法中,同一watcher只会保存一次,不同的watcher就会多次加入到queue中,pending标记用于控制setTimeout中的watcher批量执行逻辑仅执行一次,相当于防抖策略
    • nextTick使一个宏任务,相当于异步代码,当全部watcher存入队列中,就会执行内部的批量执行逻辑。

    然后需要在Watcher中封装一个run方法:

      run() {
        let newValue = this.get();
        if (this.user) {
          this.cb(vm, newValue, oldValue);
        }
      }
    
  4. flushSchedulerQueue方法。

    将刷新队列逻辑抽取出来封装到flushSchedulerQueue方法中

    function flushSchedulerQueue() {
      let flushQueue = queue.slice(0);
      flushQueue = [];
      has = {};
      peding = false;
      flushQueue.forEach((q) => q.run());
    }
    
  5. nextTick封装

    在对异步任务进行封装:

    export function nextTick(cb) {
      callbacks.push(cb);
      if (!waiting) {
        timerFunc();
        wating = true;
      }
    }
    

    针对不同的异步任务进行分类:

    let timerFunc;
    // promise
    if (Promise) {
      timerFunc = () => {
        Promise.resolve().then(flushCallbacks);
      };
        // MutationObserver
    } else if (MutationObserver) {
      let observer = new MutationObserver(flushCallbacks); // 异步执行
      let textNode = document.createTextNode(1);
      observer.observe(textNode, {
        characterData: true,
      });
      timerFunc = () => {
        textNode.textContent = 2;
      };
       // setImmediate
    } else if (setImmediate) {
      timerFunc = () => {
        setImmediate(flushCallbacks);
      };
        
      // 最后setTimeout
    } else {
      timerFunc = () => {
        setTimeout(flushCallbacks);
      };
    }
    
posted @   楸枰~  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示