页面的绘制

  • 解析 html ⽣成 DOM 树
  • 处理 css ⽣成 cssom 树
  • 将 dom 树和 cssom 树合并⽣成 render Tree(渲染树:不包括 display:none,head 节点,但是包括 visibility:hidden 的节点)
  • 根据 render Tree, 得到每个节点的⼏何信息(位置与⼤⼩:margin,padding,width,height 等)
  • 将上⾯的到的⼏何信息传送到 GPU进⾏绘制。

概述

回流(Reflow)

回流是指在渲染页面时,由于DOM结构或尺寸的改变,需要重新计算元素的几何属性和页面布局。这个过程涉及了更多的计算工作,因此比重绘开销更大。当发生回流时,浏览器需要重新计算每个受影响元素的位置和尺寸,然后重新绘制整个页面。

导致回流的操作包括:

  • 添加、删除、修改DOM元素(例如改变文本内容)
  • 改变元素的位置、尺寸、边距、边框等属性
  • 浏览器窗口大小改变
  • 激活CSS伪类(如:hover)
  • 添加或删除样式表

回流不仅影响单个元素,它可能会影响到整个渲染树,进而触发连锁回流。因此,需要尽量避免频繁的DOM和样式改变,以减少回流的次数。

重绘(Repaint)

重绘是指在元素的外观改变但几何属性没有改变时,浏览器会重新绘制元素。这个过程开销相对较小,因为不涉及布局的重新计算。浏览器只需要根据新的样式重新绘制元素,而无需重新定位和计算大小。

导致重绘的操作包括:

  • 修改元素的颜色、背景、边框等属性,但不影响其尺寸和位置
  • 激活CSS伪类(如:hover)

⚡ 如何减少 重排和重绘

✅ 样式操作优化

  1. 合并多次修改

    // ❌ 多次触发重排
    el.style.width = "100px";
    el.style.height = "100px";
    el.style.margin = "10px";
    
    // ✅ 一次性修改
    el.style.cssText = "width:100px; height:100px; margin:10px;";
    

    或者使用 classList.add() 批量切换 class。

  2. 避免频繁读取导致强制回流的属性
    比如 offsetHeightgetBoundingClientRect,要么缓存,要么用 requestAnimationFrame 节流。

    浏览器为了性能优化,会把 DOM 操作(增删改样式、布局计算)放进一个队列里,等到下一帧(16ms 左右)统一处理。

    但是,当你调用 读取布局信息的 API 时,比如:

    • getComputedStyle(element)
    • element.getBoundingClientRect()
    • element.offsetWidth / offsetHeight
    • element.scrollTop / scrollLeft

    浏览器必须 返回一个“最新、准确”的值。
    这就迫使它把队列里的修改先执行掉,再返回结果。

    这个过程就叫 强制同步布局 (Forced Synchronous Layout)。
    👉 如果 DOM 修改确实影响了布局,那么这一步就会触发 回流。


✅ DOM 操作优化

  1. 离线操作 DOM,再一次性插入

    • 使用 DocumentFragment
    • 使用 cloneNode → 修改 → 替换原节点
    const frag = document.createDocumentFragment();
    for (let i = 0; i < 1000; i++) {
      const li = document.createElement("li");
      li.textContent = i;
      frag.appendChild(li);
    }
    ul.appendChild(frag);
    
  2. 批量修改时,先 display:none → 修改 → 再显示
    隐藏时修改不会触发回流,最后只触发一次。


✅ CSS 和渲染层优化

  1. 避免使用影响范围大的 CSS

    • 比如 table 布局修改会引发整个表格回流
    • 少用 float,尽量使用 flexgrid
  2. 使用 transformopacity 来代替会触发回流的属性

    • left/top/width/height → 会回流
    • transform: translate/scale/rotateopacity → 只触发合成层变化,不回流
    /* ✅ GPU 加速,避免回流重绘 */
    transform: translateX(100px);
    opacity: 0.5;
    
  3. 开启独立合成层(让元素在单独的渲染层上操作)

    • will-change: transform;
    • transform: translateZ(0);
    • position: fixed/absolute;(部分场景,脱离文档流)

✅ JS 和渲染协调

  1. 读写分离

    // ❌ 交替读写:多次强制回流
    el.style.width = "100px";
    console.log(el.offsetHeight);
    el.style.height = "100px";
    
    // ✅ 先读后写
    const h = el.offsetHeight;
    el.style.width = "100px";
    el.style.height = "100px";
    
  2. 节流 / 防抖
    resizescrollinput 等高频事件使用节流/防抖,减少触发频率。

  3. 使用虚拟滚动 / 分页渲染

  • 渲染超大列表时,不要一次性插入上千 DOM,使用虚拟列表技术或分页。

总结: “批量操作、读写分离、用 transform/opacity、减少层级影响、用离线 DOM、节流高频操作。”

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐