이 글의 내용은 Vue2.6의 nextTick 메소드에 대한 내용입니다. 참고할만한 가치가 있으니 참고하시면 도움이 될 것 같습니다.
Vue 2.6의 nextTick 메서드에 대한 간략한 분석입니다.
Event Loop
JS의 Event Loop 및 Task Queue 실제로는 그렇습니다 nextTick의 개념을 이해하는 열쇠입니다.
사실 인터넷에 자세히 소개하는 수준 높은 글들이 많아서 간단히 짚어봤습니다.
다음 내용은 브라우저 측 JS에 적용됩니다. NodeJS의 이벤트 루프 메커니즘은 다릅니다.
사양에는 작업이 task(macrotask)와 microtask의 두 가지 범주로 나누어진다고 규정되어 있습니다. task(macrotask) 和 microtask。
通常认为是 task 的任务源:
setTimeout / setInterval setImmediate MessageChannel I/O UI rendering
通常认为是 microtask 的任务源:
Promise process.nextTick MutationObserver Object.observe(已废弃)
简单概况:(这里是官方规范)
首先开始执行 script 脚本,直到执行上下文栈为空时,开始清空 microtask 队列里的任务,队列嘛,先入先出,出一个执行一个,清空完毕,走事件循环。
事件循环:不断地去取 task 队列的中的一个任务推入栈中执行,并在当次循环里依次清空 microtask 队列里的任务,清空之后,可能会触发页面更新渲染(由浏览器决定)。
之后重复 事件循环 步骤。
nextTick
Vue 中数据的变化到 DOM 的更新渲染是一个异步过程。
此方法便用于在 DOM 更新循环结束之后执行延迟回调。
使用方法很简单:
// 修改数据
vm.msg = 'Hello';
// DOM 还没有更新
Vue.nextTick(function() {
// DOM 更新了
});
// 作为一个 Promise 使用
Vue.nextTick().then(function() {
// DOM 更新了
});
源码 去除注释,其实也只有不到一百来行,整体还是很容易理解的。
这里划成 3 个部分介绍。
介绍 引入的模块 和 定义的变量。
// noop 空函数,可用作函数占位符
import { noop } from 'shared/util';
// Vue 内部的错误处理函数
import { handleError } from './error';
// 判断是IE/IOS/内置函数
import { isIE, isIOS, isNative } from './env';
// 使用 MicroTask 的标识符
export let isUsingMicroTask = false;
// 以数组形式存储执行的函数
const callbacks = [];
// nextTick 执行状态
let pending = false;
// 遍历函数数组执行每一项函数
function flushCallbacks() {
pending = false;
const copies = callbacks.slice(0);
callbacks.length = 0;
for (let i = 0; i < copies.length; i++) {
copies[i]();
}
}接下来是核心的 异步延迟函数。这里不同的 Vue 版本采用的策略其实并不相同。
2.6 版本优先使用 microtask 作为异步延迟包装器。
2.5 版本则是 macrotask 结合 microtask。然而,在重绘之前状态改变时会有小问题(如 #6813)。此外,在事件处理程序中使用 macrotask 会导致一些无法规避的奇怪行为(如#7109,#7153,#7546,#7834,#8109)。
所以 2.6 版本现在又改用 microtask 了,为什么是又呢。。因为 2.4 版本及之前也是用的 microtask。。。
microtask 在某些情况下也是会有问题的,因为 microtask 优先级比较高,事件会在顺序事件(如#4521,#6690 有变通方法)之间甚至在同一事件的冒泡过程中触发(#6566)。
// 核心的异步延迟函数,用于异步延迟调用 flushCallbacks 函数
let timerFunc;
// timerFunc 优先使用原生 Promise
// 原本 MutationObserver 支持更广,但在 iOS >= 9.3.3 的 UIWebView 中,触摸事件处理程序中触发会产生严重错误
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve();
timerFunc = () => {
p.then(flushCallbacks);
// IOS 的 UIWebView,Promise.then 回调被推入 microtask 队列但是队列可能不会如期执行。
// 因此,添加一个空计时器“强制”执行 microtask 队列。
if (isIOS) setTimeout(noop);
};
isUsingMicroTask = true;
// 当原生 Promise 不可用时,timerFunc 使用原生 MutationObserver
// 如 PhantomJS,iOS7,Android 4.4
// issue #6466 MutationObserver 在 IE11 并不可靠,所以这里排除了 IE
} else if (
!isIE &&
typeof MutationObserver !== 'undefined' &&
(isNative(MutationObserver) ||
// PhantomJS 和 iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
let counter = 1;
const observer = new MutationObserver(flushCallbacks);
const textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
timerFunc = () => {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
isUsingMicroTask = true;
// 如果原生 setImmediate 可用,timerFunc 使用原生 setImmediate
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks);
};
} else {
// 最后的倔强,timerFunc 使用 setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0);
};
}
一句话总结优先级:microtask 优先。
Promise > MutationObserver > setImmediate > setTimeout
nextTick 函数
nextTick 函数。接受两个参数:
cb 回调函数:是要延迟执行的函数;
ctx:指定 cb 回调函数 的 this 指向;
Vue 实例方法 $nextTick 做了进一步封装,把 ctx 设置为当前 Vue 实例。
export function nextTick(cb?: Function, ctx?: Object) {
let _resolve;
// cb 回调函数会经统一处理压入 callbacks 数组
callbacks.push(() => {
if (cb) {
// 给 cb 回调函数执行加上了 try-catch 错误处理
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
// 执行异步延迟函数 timerFunc
if (!pending) {
pending = true;
timerFunc();
}
// 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve;
});
}
}
小结
整体看过来,感觉还是比较好理解的吧~ 2.6 版本相比之前简化了一点。
小结一下,每次调用 Vue.nextTick(cb)
일반적으로 task의 작업 소스로 간주됨: rrreee보통 microtask의 작업 소스로 간주됨 : #🎜🎜 #rrreee간단한 개요: (공식 사양은 다음과 같습니다)
:
task queue 에서 지속적으로 작업을 가져와 실행을 위해 스택에 푸시합니다. 현재 루프에서 microtask 대기열 의 작업을 순차적으로 지운 후 페이지 업데이트 렌더링이 트리거될 수 있습니다(브라우저에 의해 결정됨).
nextTick
Update에서 Vue의 데이터 변경 사항을 DOM으로 렌더링하는 것은 비동기 프로세스입니다.이 메서드는 DOM 업데이트 루프가 끝난 후 지연된 콜백을 실행하는 데 사용됩니다. 사용 방법은 매우 간단합니다. rrreee 소스 코드 주석이 없으면 실제로는 100줄도 채 되지 않으며 전체 내용은 여전히 이해하기 매우 쉽습니다.
3부분으로 나누어 소개합니다.Vue.nextTick(cb)가 호출될 때마다 수행되는 작업은 다음과 같습니다. #🎜🎜##🎜🎜#cb 함수 #🎜🎜#가 푸시됩니다. #🎜🎜#callbacks 배열 #🎜🎜# 처리 후 #🎜🎜#timerFunc 함수 #🎜🎜# 실행, 호출 지연 #🎜🎜#flushCallbacks 함수 #🎜🎜#, 순회 및 실행 #🎜🎜#callbacks 배열 #🎜🎜 # 모든 기능. #🎜🎜##🎜🎜#지연된 통화 우선순위는 다음과 같습니다: #🎜🎜##🎜🎜#Promise#🎜🎜# > #🎜🎜#MutationObserver#🎜🎜# > #🎜🎜#setImmediate#🎜🎜 # > ; #🎜🎜#setTimeout#🎜🎜##🎜🎜##🎜🎜##🎜🎜#버전 차이#🎜🎜##🎜🎜##🎜🎜#실제로는 #🎜🎜#Vue 2.4, 2.5, 2.6# 🎜🎜 # #🎜🎜#nextTick#🎜🎜# 버전에는 약간 다른 전략이 있습니다. #🎜🎜##🎜🎜#전체 #🎜🎜#2.6#🎜🎜#은 #🎜🎜#2.4#🎜🎜#과 비교적 유사합니다. (자세히 살펴보면 기본적으로는 똑같습니다. #🎜🎜#2.6#🎜🎜#timerFunc에는 추가 세트가 있어 즉각 판단) #🎜🎜#2.5 버전은 실제로 거의 동일합니다. . . 소스 코드는 약간 다르게 작성됩니다. Promise > setImmediate > MessageChannel > setTimeout. 첫 번째.
위 내용은 Vue2.6의 nextTick 메소드 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!