传统的问题 链接到标题
用 setInterval(fn, 16) 做动画(16ms 约等于 60 帧 / 秒),看似能对齐屏幕刷新率,但存在致命问题:
时机不准:定时器的回调执行时机由 JS 事件循环决定,即使设置 16ms,也可能因为主线程阻塞(比如有耗时任务)导致回调延迟执行,造成帧丢失、动画卡顿。
与屏幕刷新不同步:屏幕刷新率是固定的(比如 60Hz 对应每 16.67ms 刷新一次),但定时器无法感知屏幕的刷新节奏,可能出现 “帧撕裂”(比如屏幕刚刷新完,定时器才执行绘制,导致下一帧要等更久)。
无优化机制:即使页面隐藏(比如切到其他标签页),定时器仍会执行,浪费 CPU / 电池资源。
requestAnimationFrame 优势在哪 链接到标题
requestAnimationFrame (rAF) 为什么能让动画更流畅
- 精准对齐屏幕刷新率
浏览器会在每次屏幕刷新前执行 rAF 的回调函数(比如 60Hz 屏幕每 16.67ms 执行一次),确保绘制操作刚好赶上屏幕刷新,避免 “帧撕裂” 和无效绘制。
- 比如:屏幕准备刷新时,rAF 回调执行绘制逻辑,绘制结果直接被屏幕渲染,动画帧和屏幕刷新完全同步。
- 由浏览器统一调度,优先级更高
rAF 的回调会被纳入浏览器的渲染流水线(布局 → 绘制 → 合成),优先级高于普通定时器:
- 即使主线程有轻微阻塞,浏览器也会优先保证 rAF 回调在刷新前执行,减少帧丢失。
- 若当前帧时间不足(比如回调里有耗时操作),浏览器会跳过该帧,避免卡顿扩散。
- 自动适配刷新率 & 节能优化
- 不同设备的刷新率不同(60Hz/120Hz/144Hz),rAF 会自动适配,无需手动调整时间间隔(比如 120Hz 屏幕会每 8.3ms 执行一次)。
- 当页面隐藏(比如切标签页)或最小化时,rAF 会暂停执行,节省 CPU/GPU 资源,提升续航。
事件循环的任务分类 链接到标题
宏任务(Macrotask):每次事件循环执行一个,包括 script 整体代码、setTimeout/setInterval、I/O、UI 交互事件、setImmediate 等。
微任务(Microtask):宏任务执行完后,会立即清空所有微任务队列,包括 Promise.then/catch/finally、async/await、queueMicrotask、MutationObserver 等。
渲染阶段:微任务队列清空后,浏览器才会执行布局(Layout)→ 绘制(Paint)→ 合成(Composite) 等渲染操作。
rAF 回调执行在 微任务全部执行完毕后、浏览器开始渲染前;