nextTick的源码剖析

var callbacks = [];var pending = false;function nextTick(cb, ctx) {    var _resolve;    callbacks.push(function() {        if (cb) {            try {                cb.call(ctx);            } catch (e) {                handleError(e, ctx, 'nextTick');            }        } else if (_resolve) {            _resolve(ctx);        }    });    if (!pending) {        pending = true;        timerFunc();    }    if (!cb && typeof Promise !== 'undefined') {        return new Promise(function(resolve) {            _resolve = resolve;        })    }}

能够看到在 nextTick 函数中把通过参数 cb 传入的函数,做一下包装而后 push 到 callbacks 数组中。
而后用变量 pending 来保障执行一个事件循环中只执行一次 timerFunc()。
最初执行 if (!cb && typeof Promise !== 'undefined'),判断参数 cb 不存在且浏览器反对 Promise,则返回一个 Promise 类实例化对象。例如 nextTick().then(() => {}),当 _resolve 函数执行,就会执行 then 的逻辑中。
来看一下 timerFunc 函数的定义,先只看用 Promise 创立一个异步执行的 timerFunc 函数

var timerFunc;if (typeof Promise !== 'undefined' && isNative(Promise)) {    var p = Promise.resolve();    timerFunc = function() {        p.then(flushCallbacks);        if (isIOS) {            setTimeout(noop);        }    };}

在其中发现 timerFunc 函数就是用各种异步执行的办法调用 flushCallbacks 函数。
来看一下flushCallbacks 函数

var callbacks = [];var pending = false;function flushCallbacks() {    pending = false;    var copies = callbacks.slice(0);    callbacks.length = 0;    for (var i = 0; i < copies.length; i++) {        copies[i]();    }}

执行 pending = false 使下个事件循环中能nextTick 函数中调用 timerFunc 函数。
执行 var copies = callbacks.slice(0);callbacks.length = 0; 把要异步执行的函数汇合 callbacks 克隆到常量 copies,而后把 callbacks 清空。
而后遍历 copies 执行每一项函数。回到 nextTick 中是把通过参数 cb 传入的函数包装后 push 到 callbacks 汇合中。来看一下怎么包装的。

function() {    if (cb) {        try {            cb.call(ctx);        } catch (e) {            handleError(e, ctx, 'nextTick');        }    } else if (_resolve) {        _resolve(ctx);    }}

逻辑很简略。若参数 cb 有值。在 try 语句中执行 cb.call(ctx) ,参数 ctx 是传入函数的参数。 如果执行失败执行 handleError(e, ctx, 'nextTick')。
若参数 cb 没有值。执行 _resolve(ctx),因为在nextTick 函数中如何参数 cb 没有值,会返回一个 Promise 类实例化对象,那么执行 _resolve(ctx),就会执行 then 的逻辑中。

总结

到这里 nextTice 函数的主线逻辑就很分明了。定义一个变量 callbacks,把通过参数 cb 传入的函数用一个函数包装一下,在这个中会执行传入的函数,及解决执行失败和参数 cb 不存在的场景,而后 增加到 callbacks。调用 timerFunc 函数,在其中遍历 callbacks 执行每个函数,因为 timerFunc 是一个异步执行的函数,且定义一个变量 pending来保障一个事件循环中只调用一次 timerFunc 函数。这样就实现了 nextTice 函数异步执行传入的函数的作用了。

update生命周期

beforeUpdate:数据更新时调用,产生在虚构 DOM 从新渲染和打补丁之前。 你能够在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
updated:无论是组件自身的数据变更,还是从父组件接管到的 props 或者从vuex外面拿到的数据有变更,都会触发虚构 DOM 从新渲染(don曾经渲染实现)和打补丁。并在之后调用 updated。

父子组件Update生命周期和nextTick的执行程序

父的beforeUpdate->子的beforeUpdate->子的updated->父updated->nextTick回调

//父组件<template>  <div>      <space>{{msg}}</space>    <button @click="add">减少</button>  </div>  </template><script>import space from "./space"export default {  components:{    space  },  data(){    return {      msg:1    }  },  beforeUpdate(){    console.log("父beforeUpdate")  },  updated(){    console.log("父updated")  },  methods:{    add(){      this.msg++;      this.$nextTick(()=>{        console.log("next")      })    }  }  }</script>//子组件<template>  <div>    <slot></slot>  </div></template><script>export default {  beforeUpdate(){    console.log("子beforeUpdate")  },  updated(){    console.log("子updated")  },}</script>//打印后果父beforeUpdate子beforeUpdate子updated父updatednext

这也很好了解,beforeUpdate,updated尽管也是在微工作中执行,然而它所在的微工作先创立,所以先执行。
如果改成上面这样,后果就不一样了

add(){  this.$nextTick(()=>{    console.log("next")  })//next的微工作先创立,所以先执行  this.msg++;}//打印后果next父beforeUpdate子beforeUpdate子updated父updated