浏览器如何优化渲染流程中的重排和重绘?

浏览器的渲染流程涉及 HTML 解析、DOM 生成、布局计算、分层和绘制。优化包括减少强制重排、分离读写操作。

浏览器机制 困难 性能优化 浏览器 渲染

浏览器的渲染流程是一个多阶段流水线过程,分为以下核心环节:

一、浏览器渲染核心流程

  1. 解析 HTML(Parse HTML)
    • 将 HTML 文本解析为 DOM 树(文档对象模型)。
    • 遇到外部 CSS/JS 时:
      • CSS 解析在预解析线程进行 → CSS 不会阻塞 HTML 解析,构建 CSSOM 树(CSS 对象模型)。
      • JS 执行会阻塞 HTML 解析 → 需等待 JS 下载并执行完成才继续解析(JS 可能修改 DOM/CSSOM)。
  2. 生成渲染树(Render Tree)
    • 组合 DOM 树与 CSSOM 树 → 排除隐藏元素(如 display: none),生成仅包含可见元素的渲染树(Render Tree)。
  3. 布局(Layout / Reflow)
    • 计算渲染树中每个节点的精确位置和尺寸(如 width/height/position) → 生成 布局树(Layout Tree)。
  4. 分层(Layering)
    • 将布局树拆分为独立的图层(Layer)。以下元素提升为新图层:
      • 显式设置 z-index/opacity/filter 的元素(层叠上下文)。
      • 需裁剪(clip/overflow)的元素。
  5. 绘制(Paint)
    • 分层后转为绘制指令列表 → 光栅化(Rasterizing)将指令转为屏幕像素。

二、重排(Reflow)与重绘(Repaint)

  • 重排(Reflow):更改影响节点几何属性(位置、尺寸)的操作 → 重新触发完整布局阶段
    示例:修改元素的 width/height、移动元素位置。
    // 触发重排的操作
    element.style.width = '200px';
    window.getComputedStyle(element); // 强制获取布局信息(触发重排)
    
  • 重绘(Repaint):更改不影响布局的视觉样式(如 color/background) → 仅重绘当前图层
    示例:改变文字颜色、背景图。

👉 性能开销:重排 > 重绘(重排需重新计算布局)

三、优化策略

  1. 减少强制重排
    • 避免在循环中频繁读取布局数据(如 offsetHeight)→ 应一次性读取并缓存。
    • 使用 DocumentFragment 或虚拟 DOM 批量修改 DOM:
      const fragment = document.createDocumentFragment();
      for (let i = 0; i < 100; i++) {
        const node = document.createElement('div');
        fragment.appendChild(node);
      }
      document.body.appendChild(fragment); // 只触发一次重排
      
  2. 分离读写操作
    // 优化写法:先集中写操作,再读取
    element.style.width = '100px';
    element.style.height = '100px';
    requestAnimationFrame(() => {
      console.log(element.offsetHeight); // 统一读取
    });
    
  3. 提升渲染层级
    • 对高频动画元素使用 will-changetransform,创建独立图层 → 仅触发重绘:
      .animation {
        transform: translateZ(0); /* GPU 加速 */
      }
      
  4. 使用 display: none 隐藏元素
    • 临时隐藏元素时用 display: none → 移除渲染树 → 后续修改不会触发布局。
  5. 避免过深的 CSS 选择器层级:减少样式计算开销。

  6. JS 优化
    • 使用 requestAnimationFrame 执行动画 → 对齐浏览器刷新帧率(60Hz)。
  7. 合并样式修改
    • class 替换内联 style 修改,减少渲染调用:
      element.className += ' updated'; // 仅一次重排/重绘
      
  8. 减少图片懒加载的布局抖动:通过提前定义宽高避免多次重排。