Vue 源码
你想要的 vue 源码分析 · 17
什么是 tick js 引擎执行过程的运行机制 nextTick 是什么 数据从依赖收集到派发更新用到了 nextTick 机制,数据的变化到 DOM 的重新渲染是一个异步的过程,发生在下一个 tick nextTick 把要执行的任务都推到一个队列中,统一在下一个 nextTick 中执行 数据更改触发渲染 watcher 的 update,渲染 watcher 的 flushScheduler…
什么是 tick js 引擎执行过程的运行机制 nextTick 是什么 数据从依赖收集到派发更新用到了 nextTick 机制,数据的变化到 DOM 的重新渲染是一个异步的过程,发生在下一个 tick nextTick 把要执行的任务都推到一个队列中,统一在下一个 nextTick 中执行 数据更改触发渲染 watcher 的 update,渲染 watcher 的 flushScheduler…
数据从依赖收集到派发更新用到了 nextTick 机制,数据的变化到 DOM 的重新渲染是一个异步的过程,发生在下一个 tick
nextTick 把要执行的任务都推到一个队列中,统一在下一个 nextTick 中执行
数据更改触发渲染 watcher 的 update,渲染 watcher 的 flushSchedulerQueue(flushSchedulerQueue 做了队列排序,2、队列遍历 3、状态恢复等操作) 是在 nextTick 后,所以数据重新渲染是异步的
常见的一个场景:在服务端接口获取数据的时候,数据做了修改,进而引发了 DOM 重新渲染,如果想要获取变化后的 DOM,就需要在 nextTick 后执行,如下例所示
getData().then((res) => {
this.result = res.data;
this.$nextTick(() => {
let $dom = this.$refs.dom;
let html = $dom.innerHTML;
console.log(html);
});
});
// src/core/util/next-tick.js
import { noop } from 'shared/util';
import { handleError } from './error';
import { isIOS, isNative } from './env';
const callbacks = [];
let pending = false;
// 对callbacks遍历,然后执行响应的回调
// 使用callbacks队列执行回调而不是直接执行回调的原因是,保证在同一个tick内多次执行nextTick,不会开启多个异步任务
// 而把这些异步任务都压成一个同步任务,在下一个tick执行完毕
function flushCallbacks() {
pending = false;
const copies = callbacks.slice(0);
callbacks.length = 0;
for (let i = 0; i < copies.length; i++) {
copies[i]();
}
}
// 申明了microTimerFunc和macroTimerFunc2个变量
// 这两个变量分别指向microtask 的函数和 macrotask 的函数
let microTimerFunc;
let macroTimerFunc;
let useMacroTask = false;
// 对于macrotask(宏观任务)
// 检查是否支持原生setImmediate,这是一个高版本IE和Edge才支持的特性
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
macroTimerFunc = () => {
setImmediate(flushCallbacks);
};
} else if (
// 是否支持原生的MessageChannel,
typeof MessageChannel !== 'undefined' &&
(isNative(MessageChannel) ||
// PhantomJS
MessageChannel.toString() === '[object MessageChannelConstructor]')
) {
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = flushCallbacks;
macroTimerFunc = () => {
port.postMessage(1);
};
} else {
// 如过setImmediate和MessageChannel都不支持,那么就降级为setTimeout 0
macroTimerFunc = () => {
setTimeout(flushCallbacks, 0);
};
}
// 对于microtask(微观任务)
// 判断是否支持原生Promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve();
microTimerFunc = () => {
p.then(flushCallbacks);
if (isIOS) setTimeout(noop);
};
} else {
// 如果不支持,直接指向macrotask(宏观任务)
microTimerFunc = macroTimerFunc;
}
// 确保函数执行过程中对数据的任意修改,触发变化执行nextTick的时候强制走macroTimerFunc
// 比如v-on绑定一个dom事件,会强制走macrotask(宏观任务)
export function withMacroTask(fn: Function): Function {
return (
fn._withTask ||
(fn._withTask = function() {
useMacroTask = true;
const res = fn.apply(null, arguments);
useMacroTask = false;
return res;
})
);
}
// 把传入的回调函数压入callbacks数组中,最后一次性的根据useMacroTask 条件执行macroTimerFunch还是microTimerFunc
// 而他们都会在下一个tick执行flushCallbacks
export function nextTick(cb?: Function, ctx?: Object) {
let _resolve;
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
if (!pending) {
pending = true;
if (useMacroTask) {
macroTimerFunc();
} else {
microTimerFunc();
}
}
// 当nextTick不传cb参数的时候,任务状态直接变成resolve,可以用then
if (!cb && typeof Promise !== 'undefined') {
return new Promise((resolve) => {
_resolve = resolve;
});
}
}
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论