vue nextTick源码

早之前有分享过vue的nextTick的使用,当时说当数据发生变化,更新后执行回调没有实现,那时候也不知道怎么测试的,其实nextTick方法只是做了一步异步。

先明确一下,修改数据、渲染页面,在vue里面都是同步的,包括生命周期,也是同步执行,而nextTick是用异步的回调,所以才能获取最新的dom或者实例属性。

之前2.2版本只有promise、MutationObserver、setTimeout,而且是一个自执行函数,我觉得那样看的更舒服,2.x最后的版本2.6.9就不太一样了。上源码,然后使用一下。

先确定用哪种异步任务,promise>motatiosObserver>setImmediate>setTimout,这几个的使用方法可以自己查一下:

function isNative (Ctor) {
    return /native code/.test(Ctor.toString())
}
var timerFunc;
if (typeof Promise !== 'undefined' && isNative(Promise)) {
    var p = Promise.resolve();
    timerFunc = function () {
        p.then(flushCallbacks);
    };
    isUsingMicroTask = true;
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
    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)) {
    timerFunc = function () {
        setImmediate(flushCallbacks);
    };
} else {
    timerFunc = function () {
        setTimeout(flushCallbacks, 0);
    };
}

因为我们可能多次调用nextTick,多次调用也只会一起循环执行,不会调用一次执行一次,这边第一次调用nextTick就执行timerFunc,又因为timerFunc调用flushCallBacks的时候是异步的,nextTick是同步调用,所以当执行flushCallBacks的时候,callbacks是调用几个就有几个回调,pending就是这个用处。这边清空数组用length=0,以前还真不知道可以这样用。另外为什么要新声明一个copies,我觉得是防止nextTick里面再调用nextTick。

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]();
    }
}
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;
        })
    }
}
console.log(1);
nextTick(() => {
    console.log(2);
});
console.log(3);

有人会纠结事件循环是宏任务,微任务,UI渲染的顺序,那nextTick如果是微任务,怎么在UI渲染之前调用为什么还能获取最新的dom。执行下面代码看结果:

<div id="app">内容还没修改</div>
document.getElementById('app').innerText = '内容已经修改';
Promise.resolve().then(() => {
    alert(document.getElementById('app').innerText + 'promise')
})
setTimeout(() => {
    alert(document.getElementById('app').innerText + 'setTimeout')
})
alert(document.getElementById('app').innerText + 'end')

所以大胆得出一个结论,宏任务,微任务,UI渲染没有错,js修改了dom之后,在js里面去获取的时候,根据的是js对dom操作的结果,UI渲染只是页面的展示,并不影响js对dom元素的获取和操作。

上面是vue nextTick的源码,讲真,看别人源码还要去看一些api,然后分析为什么这样写,看着代码看明白了,一关上,就一点写不出来。

(完)

本文分享自微信公众号 - coding个人笔记(gh_2ce38b49dae1),作者:coding个人笔记

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ES6之class的继承

    class语法为我们提供了构造函数的语法糖,响应的,也给我们提供了ES5通过原型链实现继承提供了extends关键字实现继承。继承这个概念对面后台应该也是非常常...

    wade
  • 我理解的JavaScript预编译

    JavaScript执行过程首先先语法分析,就是分析一遍代码有没有语法错误,解析期间不会执行代码。接着就开始预编译,预编译完了就开始一行一行执行代码。

    wade
  • 链式调用this

    jQuery在JavaScript库中的一哥地位是不可撼动的,虽然随着这几年框架的崛起和一些大平台移除了jQuery的依赖,但不可否认jQuery还是前端开发必...

    wade
  • 修炼内功之JavaScript设计模式(一)

    有一个简单的大局观,造完了火箭,再回归正文,我们的日常生活和工作中的大部分还是需要脚踏实地搬砖的,为了应对不断变换的需求,为了不加班,掌握设计模式的思想可以大大...

    童欧巴
  • js判断方法是否存在

    似水的流年
  • JavaScript设计模式--简单工厂模式

    工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型...

    wfaceboss
  • jQuery on()方法

    以上三种方法在jQuery1.8之后都不推荐使用,官方在1.9时已经取消使用live()方法了,所以建议都使用on()方法。

    山河木马
  • js闭包探秘

    闭包(Closure) 是javascript这门语言中有些复杂并且充满误解的特性。简言之,闭包是一个对象,这个对象包含一个方法(function)和该方法创建...

    前端博客 : alili.tech
  • JQuery事件处理

    Jquery事件 1、  绑定事件示例代码: <a href=”#”>绑定事件</a> <div style=”display:none;”> 什么是绑定事件?...

    苦咖啡
  • 知识图谱可视化前奏之d3.js

    0.说在前面1.d3.js初识2.绘制完整的柱形图3.让图表动起来4.浅析Update、Enter、Exit5.交互式操作6.作者的话

    公众号guangcity

扫码关注云+社区

领取腾讯云代金券