• 因为Vue的异步更新队列,$nextTick是用来知道什么时候DOM更新完成的。
    • Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。

    实现原理

    • 先判断是否原生支持promise,如果支持,则利用promise来触发执行回调函数;
    • 否则如果支持MutationObserver,则实例化一个观察者对象,观察文本节点发生变化时,触发执行所有回调函数。
    • 如果都不支持,则利用setTimeout设置延时为0。
    • 每次event loop的最后,会有一个UI render,也就是更新DOM
    • 只要让nextTick里的代码放在UI render步骤后面执行,就能访问到更新后的DOM了。

    https://blog.csdn.net/qq_38290251/article/details/107550899

    1. var nextTick = (function () {
    2. var callbacks = []; // 存储需要触发的回调函数
    3. var pending = false; // 是否正在等待的标识(false:允许触发在下次事件循环触发callbacks中的回调, true: 已经触发过,需要等到下次事件循环)
    4. var timerFunc; // 设置在下次事件循环触发callbacks的 触发函数
    5. //处理callbacks的函数
    6. function nextTickHandler () {
    7. pending = false;// 可以触发timeFunc
    8. var copies = callbacks.slice(0);//复制callback
    9. callbacks.length = 0;//清空callback
    10. for (var i = 0; i < copies.length; i++) {
    11. copies[i]();//触发callback回调函数
    12. }
    13. }
    14. //如果支持Promise,使用Promise实现
    15. if (typeof Promise !== 'undefined' && isNative(Promise)) {
    16. var p = Promise.resolve();
    17. var logError = function (err) { console.error(err); };
    18. timerFunc = function () {
    19. p.then(nextTickHandler).catch(logError);
    20. // ios的webview下,需要强制刷新队列,执行上面的回调函数
    21. if (isIOS) { setTimeout(noop); }
    22. };
    23. //如果Promise不支持,但是支持MutationObserver(h5新特性,异步,当dom变动是触发,注意是所有的dom都改变结束后触发)
    24. } else if (typeof MutationObserver !== 'undefined' && (
    25. isNative(MutationObserver) ||
    26. // PhantomJS and iOS 7.x
    27. MutationObserver.toString() === '[object MutationObserverConstructor]'
    28. )) {
    29. // use MutationObserver where native Promise is not available,
    30. // e.g. PhantomJS IE11, iOS7, Android 4.4
    31. var counter = 1;
    32. var observer = new MutationObserver(nextTickHandler);
    33. //创建一个textnode dom节点,并让MutationObserver 监视这个节点;而 timeFunc正是改变这个dom节点的触发函数
    34. var textNode = document.createTextNode(String(counter));
    35. observer.observe(textNode, {
    36. characterData: true
    37. });
    38. timerFunc = function () {
    39. counter = (counter + 1) % 2;
    40. textNode.data = String(counter);
    41. };
    42. } else {// 上面两种不支持的话,就使用setTimeout
    43. timerFunc = function () {
    44. setTimeout(nextTickHandler, 0);
    45. };
    46. }
    47. //nextTick接受的函数, 参数1:回调函数 参数2:回调函数的执行上下文
    48. return function queueNextTick (cb, ctx) {
    49. var _resolve;//用于接受触发 promise.then中回调的函数
    50. //向回调数据中pushcallback
    51. callbacks.push(function () {
    52. //如果有回调函数,执行回调函数
    53. if (cb) { cb.call(ctx); }
    54. if (_resolve) { _resolve(ctx); }//触发promise的then回调
    55. });
    56. if (!pending) {//是否执行刷新callback队列
    57. pending = true;
    58. timerFunc();
    59. }
    60. //如果没有传递回调函数,并且当前浏览器支持promise,使用promise实现
    61. if (!cb && typeof Promise !== 'undefined') {
    62. return new Promise(function (resolve) {
    63. _resolve = resolve;
    64. })
    65. }
    66. }
    67. })();
    68. mounted: function () {
    69. this.message = "abc"
    70. this.$nextTick(function () {
    71. console.log(this.message)
    72. })
    73. }