2026/4/7 4:38:02
网站建设
项目流程
瑞安市建设工程质量监督站网站,wordpress 文件类型,外贸招聘网站,怎么免费做网站在前端开发中#xff0c;微任务#xff08;Microtask#xff09;和宏任务#xff08;Macrotask#xff09;是异步编程的核心概念。理解它们的执行机制不仅能帮你写出更高效的代码#xff0c;更是面试中的高频考点。本文将结合Vue3源码级案例#xff0c;深入探讨它们的区…在前端开发中微任务Microtask和宏任务Macrotask是异步编程的核心概念。理解它们的执行机制不仅能帮你写出更高效的代码更是面试中的高频考点。本文将结合Vue3源码级案例深入探讨它们的区别与最佳实践。一、核心概念事件循环的双队列机制JavaScript引擎通过事件循环处理异步任务其中包含两条关键队列宏任务队列setTimeout、setInterval、DOM事件、AJAX回调等微任务队列Promise.then、MutationObserver、queueMicrotask等执行规则当前宏任务执行完毕后会立即清空所有微任务然后才会执行下一个宏任务。二、案例实战从基础到Vue3应用案例1基础执行顺序面试必考// strict-mode TypeScript console.log(1. 同步代码开始); setTimeout((): void { console.log(5. 宏任务setTimeout); }, 0); Promise.resolve().then((): void { console.log(3. 微任务Promise.then); }); console.log(2. 同步代码结束); /** * 输出顺序 * 1. 同步代码开始 * 2. 同步代码结束 * 3. 微任务Promise.then * 5. 宏任务setTimeout */面试要点解释为什么微任务优先于宏任务执行。这是事件循环的标准行为微任务在当前任务结束后立即执行而宏任务需要等待下一次循环。案例2Vue3的nextTick实现原理源码级分析// Vue3 nextTick 简化版源码实现 const resolvedPromise: Promiseany Promise.resolve(); let currentFlushPromise: Promisevoid | null null; function nextTickT void( this: T, fn?: (this: T) void ): Promisevoid { const p currentFlushPromise || resolvedPromise; return fn ? p.then(this ? fn.bind(this) : fn) : p; } // 实际应用案例 import { ref, nextTick } from vue; const count ref(0); async function updateData(): Promisevoid { console.log(1. 修改数据); count.value 100; console.log(2. DOM尚未更新, document.querySelector(.count)?.textContent); // 方法一使用Vue的nextTick await nextTick(); console.log(4. nextTick后DOM已更新, document.querySelector(.count)?.textContent); // 方法二手动Promise实现不推荐 Promise.resolve().then(() { console.log(5. Promise也能获取更新后的DOM, document.querySelector(.count)?.textContent); }); setTimeout(() { console.log(6. setTimeout获取DOM, document.querySelector(.count)?.textContent); }, 0); } updateData(); console.log(3. 同步代码继续执行); /** * 输出顺序分析 * 1. 修改数据 * 2. DOM尚未更新 ( Vue批量更新机制DOM未变化 ) * 3. 同步代码继续执行 * 4. nextTick后DOM已更新 ( 微任务阶段DOM已更新 ) * 5. Promise也能获取更新后的DOM ( 微任务阶段 ) * 6. setTimeout获取DOM ( 宏任务阶段 ) */使用时机在Vue3中当你需要在DOM更新后执行某些操作时必须使用nextTick。它比手动的Promise.resolve()更可靠因为Vue会统一管理更新队列。案例3Vue3组件高频更新场景template div input v-modelsearchQuery placeholder搜索... / ul li v-foritem in filteredList :keyitem.id {{ item.name }} /li /ul /div /template script setup langts import { ref, computed, nextTick } from vue; interface ListItem { id: number; name: string; } const searchQuery refstring(); const list refListItem[]([ { id: 1, name: Apple }, { id: 2, name: Banana }, // ... 大量数据 ]); // 计算属性自动缓存 const filteredList computedListItem[](() { return list.value.filter(item item.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ); }); // 错误示范用宏任务处理更新 function badPractice(): void { searchQuery.value new value; setTimeout(() { // 问题用户可能看到中间状态 console.log(DOM已更新但可能闪烁); performAfterDOMUpdate(); }, 0); } // 正确示范用微任务/nextTick async function goodPractice(): Promisevoid { searchQuery.value new value; await nextTick(); // 优势确保所有DOM更新完成 console.log(DOM稳定后执行); performAfterDOMUpdate(); } function performAfterDOMUpdate(): void { // 获取更新后的DOM尺寸 const element document.querySelector(ul); if (element) { console.log(列表高度:, element.clientHeight); } } /script使用时机在处理高频更新的场景如搜索框、滚动加载时必须使用微任务来确保DOM状态的一致性。宏任务会导致UI闪烁和状态不同步。案例4表单验证与错误聚焦实际业务template form submit.preventhandleSubmit input refusernameInput v-modelform.username / span v-iferrors.username{{ errors.username }}/span input refemailInput v-modelform.email / span v-iferrors.email{{ errors.email }}/span button typesubmit提交/button /form /template script setup langts import { ref, reactive, nextTick } from vue; interface FormData { username: string; email: string; } interface FormErrors { username?: string; email?: string; } const form reactiveFormData({ username: , email: }); const errors reactiveFormErrors({}); const usernameInput refHTMLInputElement | null(null); const emailInput refHTMLInputElement | null(null); async function handleSubmit(): Promisevoid { // 清空错误 Object.keys(errors).forEach(key { delete errors[key as keyof FormErrors]; }); // 验证 if (!form.username) { errors.username 用户名不能为空; } if (!form.email.includes()) { errors.email 邮箱格式错误; } // 关键错误信息渲染后聚焦 await nextTick(); // 微任务等待错误信息渲染到DOM if (errors.username) { usernameInput.value?.focus(); } else if (errors.email) { emailInput.value?.focus(); } else { // 提交表单 await submitForm(); } } async function submitForm(): Promisevoid { try { // API调用 console.log(表单提交:, form); } catch (error) { // 处理提交错误 errors.email 提交失败请重试; await nextTick(); emailInput.value?.focus(); } } /script使用时机在需要操作DOM元素如聚焦、滚动、测量尺寸时必须在数据变更后使用nextTick。这是Vue开发中的最佳实践。案例5性能优化 - 防抖与微任务结合// useDebounce.ts - 组合式API import { ref } from vue; export function useDebouncedCallbackT extends (...args: any[]) any( callback: T, delay: number 300 ) { const timer refnumber | null(null); return (...args: ParametersT): void { if (timer.value ! null) { clearTimeout(timer.value); } timer.value window.setTimeout(() { // 宏任务防抖后的最终执行 callback(...args); }, delay); }; } // 在组件中使用 import { ref, watch, nextTick } from vue; import { useDebouncedCallback } from ./useDebounce; const searchKeyword refstring(); // 防抖搜索请求 const debouncedSearch useDebouncedCallback(async (keyword: string): Promisevoid { console.log(发送搜索请求:, keyword); await fetchSearchResults(keyword); }, 500); watch(searchKeyword, (newVal: string): void { // 宏任务避免高频触发 debouncedSearch(newVal); // 微任务立即更新UI状态 nextTick().then(() { updateUIState(searching); }); }); function updateUIState(state: string): void { const indicator document.querySelector(.search-indicator); if (indicator) { indicator.textContent 状态: ${state}; } }使用时机宏任务防抖适合控制请求频率微任务适合立即响应用户交互。两者结合可以同时保证性能和用户体验。案例6面试高频题 - 复杂异步场景// 面试题分析输出顺序 async function interviewQuestion(): Promisevoid { console.log(1. 开始); setTimeout(() console.log(8. setTimeout 1), 0); const promise1 new Promisevoid((resolve) { console.log(2. Promise构造函数); resolve(); }); promise1.then(() { console.log(5. promise1.then); setTimeout(() console.log(9. setTimeout 2), 0); }); await Promise.resolve().then(() { console.log(6. await前的微任务); }); console.log(7. await后等同于微任务之后); } interviewQuestion(); Promise.resolve().then(() console.log(4. 外层微任务)); setTimeout(() console.log(10. 外层setTimeout), 0); console.log(3. 同步代码结束); /** * 输出顺序 * 1. 开始 * 2. Promise构造函数 * 3. 同步代码结束 * 4. 外层微任务 * 5. promise1.then * 6. await前的微任务 * 7. await后等同于微任务之后 * 8. setTimeout 1 * 9. setTimeout 2 * 10. 外层setTimeout */ // 解析 // 1-3: 同步代码 // 4-7: 微任务队列按注册顺序执行 // 8-10: 宏任务队列按注册顺序执行面试技巧await后面的代码会等待当前Promise解析后执行但它本身会注册一个微任务。记住await 创建Promise 注册then回调。案例7Vue3组合式API中的资源清理import { onMounted, onUnmounted, ref } from vue; interface WebSocketConfig { url: string; reconnectDelay?: number; } export function useWebSocket(config: WebSocketConfig) { const socket refWebSocket | null(null); const message refstring(); const isConnected refboolean(false); const connect (): void { socket.value new WebSocket(config.url); socket.value.onopen (): void { console.log(WebSocket连接成功); isConnected.value true; // 微任务连接后立即发送初始化数据 Promise.resolve().then(() { socket.value?.send(JSON.stringify({ type: init })); }); }; socket.value.onmessage (event: MessageEvent): void { message.value event.data; // 微任务处理消息后更新UI Promise.resolve().then(() { updateMessageUI(event.data); }); }; socket.value.onclose (): void { isConnected.value false; // 宏任务延迟重连避免立即尝试 setTimeout(() { console.log(尝试重连...); connect(); }, config.reconnectDelay || 3000); }; }; const sendMessage (data: string): void { if (socket.value?.readyState WebSocket.OPEN) { socket.value.send(data); } else { // 微任务等待连接建立后发送 Promise.resolve().then(() { if (socket.value?.readyState WebSocket.OPEN) { socket.value.send(data); } }); } }; const updateMessageUI (msg: string): void { const container document.querySelector(.message-container); if (container) { container.scrollTop container.scrollHeight; // 滚动到底部 } }; onMounted(() { connect(); }); onUnmounted(() { socket.value?.close(); }); return { message, isConnected, sendMessage }; }使用时机微任务用于需要立即执行但又不阻塞主线程的操作如连接初始化、消息预处理。宏任务用于需要延迟执行的操作如重连机制。三、使用时机决策树当你在Vue3项目中面临选择时参考以下决策树需要异步操作吗 ├── 否 → 同步执行 └── 是 → 需要立即执行吗 ├── 是 → 需要操作DOM吗 │ ├── 是 → 使用 nextTick() (微任务) │ └── 否 → 使用 Promise.resolve() (微任务) └── 否 → 需要延迟执行吗 ├── 是 → 使用 setTimeout (宏任务) └── 否 → 需要控制执行频率吗 ├── 是 → 使用防抖/节流 (宏任务) └── 否 → 考虑是否可同步执行四、面试加分项手写nextTick// 面试题手写一个简化版nextTick const resolvedPromise: Promisevoid Promise.resolve(); let currentFlushPromise: Promisevoid | null null; let pending: boolean false; const callbacks: Array() void []; function flushCallbacks(): void { pending false; const copies callbacks.slice(0); callbacks.length 0; for (const callback of copies) { callback(); } } function nextTick(cb?: () void): Promisevoid { return new Promise((resolve) { const wrappedCallback () { cb?.(); resolve(); }; callbacks.push(wrappedCallback); if (!pending) { pending true; // 关键使用微任务 currentFlushPromise resolvedPromise.then(flushCallbacks); } }); } // 测试 nextTick(() console.log(回调1)); nextTick(() console.log(回调2)); console.log(同步代码); /** * 输出 * 同步代码 * 回调1 * 回调2 */五、总结最佳实践场景推荐方案避免方案原因DOM更新后操作nextTick()setTimeout微任务更快更可靠高频事件处理防抖宏任务 微任务更新UI直接绑定事件平衡性能与体验错误处理微任务收集信息宏任务处理保持上下文一致性初始化逻辑同步或微任务宏任务避免延迟导致状态不一致资源清理onUnmounted 宏任务仅依赖微任务确保组件卸载后不再执行掌握微任务与宏任务的区别不仅能帮你通过面试更能让你在Vue3开发中写出高性能、可维护的代码。记住核心原则微任务追求即时性宏任务追求控制权。