Appearance
用 queueMicrotask 还是 requestAnimationFrame
本节目标:理解“用 queueMicrotask 还是 requestAnimationFrame”的核心思路,并能把它用于实际开发或面试表达。 短答: 更合适。 因为我们只是把 core 的内部状态“同步到响应式 state”(纯数据更新),希望在同一个事件循环内、下一拍微任务就合并写回,不延迟到下一帧。rAF 会等到下一次重绘前(一帧后的宏任务),多出一帧的延迟,还会在后台标签页被节流/暂停,而微任务不会。
为什么选 queueMicrotask
- 时序更贴合 Vue 3 的调度Vue 的 nextTick/job flush 本身就跑在微任务上。我们用 queueMicrotask 合并写回,能让:
- 本轮同步/Promise 链的多次 sync() 合并成一次,
- 然后在同一个宏任务里触发依赖更新,不必等到下一帧。
- **避免 UI 延迟与“看见旧值”**rAF 至少晚一个 frame。像 blur → finalize → 校验 这种提交点,如果用 rAF,外部 watch/校验提示可能滞后一帧,交互上会“慢半拍”。
- SSR/Node/测试环境更稳****rAF 在 SSR/Node 不存在、在后台标签会被节流;queueMicrotask 在这些环境都能工作(实在不行还可以 Promise.resolve().then 兜底)。
- 与异步校验的相对顺序更可控你现在是 core.validate().then(sync)。then 本身是微任务;sync 再 queueMicrotask,两者会在同一宏任务内按顺序执行完(不会拖到下一帧)。若换成 rAF,可能导致校验已完成但界面晚一帧才反映。
何时才考虑 requestAnimationFrame?
- 你要做读/写布局或动画节奏控制(如测量 DOM 尺寸、避免 layout thrash),需要把写入放到下一帧前统一批处理时;
- 或者你确实想有意识地把更新延迟到下一帧(视觉合成前)。
当前场景只是写响应式数据,没有测量/动画,不需要 rAF。
简洁实现(含兜底)
// 用微任务合并多次 sync;同一宏任务只进来一次let pending = false const scheduleMicrotask: (fn: () => void) => void = typeof queueMicrotask === 'function' ? queueMicrotask // 兜底:老环境用 Promise 微任务 : (fn) => Promise.resolve().then(fn) const sync = () => { if (pending) return pending = true scheduleMicrotask(() => { pending = false // 真正的状态写回(一次性) Object.assign(state, { rawValue: core.state.rawValue, formattedValue: core.state.formattedValue, isValid: core.state.isValid, errors: [...core.state.errors], validating: core.state.validating, }) })}
