vue2之nextTick源码口述

196 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

简介

版本:2.6.14

目录:src/core/util/next-tick.js

核心:异步执行回调

变量作用

  • callbacks:用来保存 nextTick 的回调函数的数组
  • pending:防止多次调用 timerFunc
  • flushCallbacks:真正执行 callbacks 的函数,会将 callbacks 清空,重置 pending
  • timerFunc:初始化成合适当前环境的异步函数,目标是触发 flushCallbacks

timerFunc 的封装

判断基本大同小于,这边只说基于不同情况下 timerFunc 的封装

  1. Promise:promises.resolve().then 传入 flushCallbacks,使其进入微任务队列。还会根据是否 ios 的判断,使用 setTimeout 去执行一个空函数 noop

    • 为什么要用触发 setTimeout 触发一个空函数?是因为在 ios 的 UIWebViews 环境下,回调推送到微任务队列后不会立即刷新,通过添加空定时器来强制刷新微任务队列
  2. MutationObserver:传入 flushCallbacks 到 MutationObserver 实例,创建一个 text 元素并监听,timerFunc 被调用的时候,会修改 text 元素,然后会触发 flushCallbacks

  3. setImmediate:直接 setImmediate(flushCallbacks)

  4. setTimeout:直接 setTimeout(flushCallbacks, 0)

其中进入 Promise 和 MutationObserver 判断分支,还会标记 isUsingMicroTask 为 true,用来修复 dom 触发两次事件回调,具体可以看src/platforms/web/runtime/modules/events.js的 add 函数

nextTick

Vue.nextTick 和 Vue.prototype.$nextTic 具体差别是后者会多传一个 this

nextTick 函数如下

export function nextTick(cb?: Function, ctx?: Object) {
  let _resolve;
  // push进去callbacks
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx);
      } catch (e) {
        handleError(e, ctx, "nextTick");
      }
    } else if (_resolve) {
      _resolve(ctx);
    }
  });
  // 防止多次调用timerFunc
  if (!pending) {
    pending = true;
    timerFunc();
  }
  // 如果没有回调函数,并且支持Promise,那就返回Promise实例
  if (!cb && typeof Promise !== "undefined") {
    return new Promise((resolve) => {
      _resolve = resolve;
    });
  }
}

整个执行顺序 nextTick->timerFunc->flushCallbacks

最终flushCallbacks会循环调用里面的回调函数,并把相关数据初始化