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,然后分析为什么这样写,看着代码看明白了,一关上,就一点写不出来。
(完)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android手势左右滑动效果
- Android开发实现根据包名判断App运行状态的方法
- Android实现朋友圈多图显示功能
- Android实现朋友圈评论回复列表
- android调用webservice接口获取信息
- RecyclerView嵌套RecyclerView完美实现京东tab吸顶效果
- Android使用SmsManager实现短信发送功能
- Kotlin结合Rxjava+Retrofit实现极简网络请求的方法
- 用Android Studio3.0新功能加快构建速度
- Android实现图片添加阴影效果的2种方法
- Android Webview与ScrollView的滚动兼容及留白处理的方法
- Android AOP框架AspectJ使用详解
- android仿Adapter实现自定义PagerAdapter方法示例
- android绘制几何图形的实例代码
- Android ListView控件使用方法