• Vue nextTick 是何原理?


    面试被大神问到 nextTick 回调的时候,就是挂载结束的时候,可以获取到准确的dom节点

    实现的原理是什么?今天做一个总结回复大神。。

    网上找了这个问题,找到几篇相关的文章看了之后发现和源码的实现是不一致的,然后记录下读码过程,方便以后翻阅再补充。

    先看vue源码的 nextTick 方法(部分单词借助了百度翻译)

      
    // 声明公共数组,存储nextTick回调函数
      var callbacks = [];
      var pending = false;
      // 执行timerFunc函数时执行这个回调函数,处理在执行nextTick时新增的方法
      function flushCallbacks () {
        pending = false;
        var copies = callbacks.slice(0);
        callbacks.length = 0;
        for (var i = 0; i < copies.length; i++) {
          copies[i]();
        }
      }

    // 定义全局的timerFunc var timerFunc; // nextTick作为行为杠杆,和微任务队列,原生本地Promise事件,或者是 // MutationObserver事件 // MutationObserver 事件有兼容性问题,在IOS >= 9.3.3 ,这个行为会被完全阻止,所 // 以,如果客户端有Promise支持的时候,我们使用Promise更好 // 优先判断是否支持Promise if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve(); timerFunc = function () { p.then(flushCallbacks); // 此处大概的意思是:在webView中,事件Promise.then 不会完全中断,会陷入一种 // 奇怪的状态,我们需要让微任务队列继续执行,所以添加一个宏任务空计时器刷新 // 微任务队列,为啥这样可以刷新请百度搜索浏览器eventloop if (isIOS) { setTimeout(noop); } }; isUsingMicroTask = true; // 如果是IE判断是否支持MutationObserver } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // 原生Promise不被支持的时候,使用MutationObserver, // e.g. PhantomJS, iOS7, Android 4.4 // (#6466 MutationObserver is unreliable in IE11) var counter = 1; var observer = new MutationObserver(flushCallbacks); var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = String(counter); }; isUsingMicroTask = true; } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // 原生如果支持 setImmediate 还是先考虑,比setTimeout好些 timerFunc = function () { setImmediate(flushCallbacks); }; } else { // 最后实在都不支持,再用setTimeout。 timerFunc = function () { setTimeout(flushCallbacks, 0); }; } // 这里是 nextTick 函数具体实现,$nextTick就是它 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); } }); // 重点是这里判断,如果现在是在执行渲染结束的情况,渲染结束了,开始调用 // 上面被赋值好的 timerFunc ,执行这个函数会 // 触发执行 flushCallbacks 这个函数,他会遍历执行全部的callbacks // 为什么会有那么多的callback呢,因为nextTick每次被执行都会在callbacks中 // 推送一个事件,形成一个事件组就是 callbacks // 这里的pending 是一个全局的变量,默认值false,在flushCallBacks里会把 // pending = false;此处是一个锁保证nextTick仅有一次执行。 if (!pending) { pending = true; timerFunc(); } // 如果没有回调函数,vue会让nextTick返回一个promise对象返回结果 if (!cb && typeof Promise !== 'undefined') { return new Promise(function (resolve) { _resolve = resolve; }) } }

    看到这里,差不多明白是在选择一个可以是微任务或者宏任务的事件,找到这个事件等待nextTick触发。

    还有一部分代码是在收集nextTick方法的回调函数

    等待时机在有一部分代码执行这些函数。

    还有一处不太明白,MutationObserver iOS5.1之前不支持,IE只有11支持,谷歌大部分支持,支持率还是很高的。

    但是如何使用这个api呢?继续看

    vue用到了MutationObserver,在使用中可以观察一部分你想观察的节点组,

    还可以设置有哪些特性发生变化的时候回调函数会获取到。

    // 找到一个要去观察的节点
    
    var targetNode = document.getElementById('#app');
    
    // 配置中添加需要观察的项
    var config = { attributes: true, childList: true, subtree: true };
    
    // 节点发生变化,回调函数会被执行
    var callback = function(mutationsList) {
        for(var mutation of mutationsList) {
            if (mutation.type == 'childList') {
                console.log('节点被添加或是被移除惹。');
            }
            else if (mutation.type == 'attributes') {
                console.log('这个 ' + mutation.attributeName + ' 被修改。');
            }
        }
    };
    
    // 创建一个观察dom变化的实例,关联到回调函数,callback是关键必填
    var observer = new MutationObserver(callback);
    
    // 使用实例的方法把观察的区域和项目关联到一起
    observer.observe(targetNode, config);
    
    // 调用这个方法可以解除观察,
    observer.disconnect();
    

    这里基本上清楚了,vue里面是观察了一段代码 textnode + count 和 修改count,

    如果可以执行nextTick的时候,它就去执行改变count随后触发 MutationObserve的回调函数 flushCallbacks()。

    看到这里可以了解到基本实现原理,回调事件使用了新增微任务或最差新增红任务的方式,这个任务是在微任务队列中的最后一个任务

    是和事件队列有关,需要确定的就是任务执行的时机,在最后一个微任务或者第一个宏任务的时候。

    这样执行回调是最好的。

    谢谢,不对的请指点。

  • 相关阅读:
    ubuntu 16.04 更新后搜狗输入法无法输入中文的问题
    转: 苹果APNS的说明
    转:svn 更新指定文件夹
    转: Java 应用一般架构
    【原创】关于bug反正出现的问题可能
    App开发者博客之: 包建强 (专注移动app开发)
    转: 阿里跨平台移动开发工具Weex
    【原创】存储层设计的一些方法论
    转:车牌的自动截取与识别方案
    转: java web demo的示例
  • 原文地址:https://www.cnblogs.com/the-last/p/11498057.html
Copyright © 2020-2023  润新知