requestAnimationFrame和requestIdleCallback优化动画和后台任务总结

一、requestAnimationFrame(rAF)优化动画

1. 核心原理

requestAnimationFrame 是浏览器为动画设计的专用 API,其回调函数会在浏览器下一次重绘之前执行(通常每秒 60 次,与屏幕刷新率同步),确保动画流畅且避免不必要的渲染。

2. 适用场景

  • DOM 动画:元素位移、缩放、透明度变化。
  • Canvas/WebGL 渲染:游戏、数据可视化。
  • 高频交互:滚动、拖拽、手势操作。

3. 优化实践

function animate() {
  // 执行动画逻辑(如更新元素位置)
  element.style.transform = `translateX(${position}px)`;
  
  // 循环调用以持续动画
  position += 1;
  requestAnimationFrame(animate);
}

// 启动动画
requestAnimationFrame(animate);

4. 关键优化技巧

  • 避免在 rAF 中执行耗时操作:将复杂计算拆分到 Web Workers 或空闲时段。
  • 批量 DOM 操作:减少布局抖动(Layout Thrashing)。
  • 使用 transformopacity:触发 GPU 加速,跳过重排/重绘。

二、requestIdleCallback(rIC)优化后台任务

1. 核心原理

requestIdleCallback 允许在浏览器空闲时段执行低优先级任务,避免阻塞关键渲染和事件处理。回调函数接收 IdleDeadline 对象,包含剩余空闲时间(timeRemaining())。

2. 适用场景

  • 日志上报:用户行为统计。
  • 数据预处理:如分页数据预加载。
  • 非关键 UI 更新:如侧边栏内容加载。

3. 优化实践

function processTask(deadline) {
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    const task = tasks.pop();
    executeTask(task); // 执行单个任务
  }
  
  if (tasks.length > 0) {
    requestIdleCallback(processTask); // 继续处理剩余任务
  }
}

// 启动后台任务
requestIdleCallback(processTask);

4. 关键优化技巧

  • 任务分片:将大任务拆分为多个小任务,每次空闲时段处理一部分。
  • 超时控制:通过 timeout 参数确保任务最终执行(慎用,可能阻塞主线程)。
    requestIdleCallback(processTask, { timeout: 1000 }); // 最多等待 1 秒
    
  • 优先级管理:结合 shouldYield 模式判断是否让出主线程。
    if (deadline.timeRemaining() <= 0) {
      requestIdleCallback(processTask); // 剩余时间不足,重新调度
      return;
    }
    

三、协同使用 rAF 和 rIC 的策略

场景示例:滚动列表加载数据

  1. 滚动动画使用 rAF:确保滚动流畅。
  2. 数据加载使用 rIC:在空闲时段加载后续数据。
// 处理滚动动画
function handleScroll() {
  requestAnimationFrame(() => {
    listContainer.style.transform = `translateY(${scrollPos}px)`;
  });
}

// 空闲时加载数据
function loadData(deadline) {
  while (deadline.timeRemaining() > 0 && hasMoreData) {
    fetchNextDataChunk(); // 加载下一块数据
  }
  if (hasMoreData) {
    requestIdleCallback(loadData);
  }
}

// 监听滚动事件
window.addEventListener('scroll', handleScroll);
// 初始加载数据
requestIdleCallback(loadData);

四、高级技巧与注意事项

1. 避免常见陷阱

问题 解决方案
rAF 中阻塞主线程 将复杂逻辑移至 Web Workers 或分帧处理。
rIC 任务未完成 记录任务状态,下次空闲时继续处理。
过度使用 rIC 限制后台任务数量,避免长期占用空闲时段。

2. 兼容性处理

  • rAF 降级:用 setTimeout 模拟(无法保证帧率)。
    const rAF = window.requestAnimationFrame || (cb => setTimeout(cb, 1000/60));
    
  • rIC 降级:用 setTimeout 或立即执行。
    const rIC = window.requestIdleCallback || (cb => setTimeout(() => cb({ timeRemaining: () => Infinity }), 0));
    

3. 性能监控

  • Chrome DevTools
    • Performance 面板:分析动画帧率(FPS)和任务执行时间。
    • Scheduling 面板:查看空闲时段利用率。
  • Long Tasks API:检测超过 50ms 的任务。
    const observer = new PerformanceObserver(list => {
      list.getEntries().forEach(entry => {
        console.log('Long task:', entry.duration);
      });
    });
    observer.observe({ entryTypes: ['longtask'] });
    

五、总结

  • requestAnimationFrame:为动画设计,确保与屏幕刷新同步,避免丢帧。
  • requestIdleCallback:处理后台任务,利用空闲时间,避免阻塞关键操作。
  • 协作模式
    • 动画/交互rAF 优先,保证流畅性。
    • 非关键任务rIC 调度,提升整体响应速度。

通过合理分配任务优先级和资源,可显著提升复杂 Web 应用的性能和用户体验。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容