当前位置:首页 > 前端 > 正文内容

重排(Reflow)与重绘(Repaint)的区别?以及如何减少重排和重绘?

virtualman2周前 (08-24)前端97

重排(Reflow)与重绘(Repaint):前端性能优化的基石

在网页开发中,我们经常听到“重排”(Reflow)和“重绘”(Repaint)这两个术语。它们是浏览器渲染页面的核心过程,也是影响页面性能的关键因素。理解它们的区别和触发条件,是进行前端性能优化的基础。


一、浏览器渲染流程简述

在深入重排和重绘之前,先简单回顾一下浏览器渲染页面的基本流程:

  1. 解析 HTML:生成 DOM 树。
  2. 解析 CSS:生成 CSSOM 树。
  3. 合并 DOM 和 CSSOM:生成渲染树(Render Tree)。
  4. 布局(Layout):计算渲染树中每个节点的几何位置(坐标、尺寸等)。
  5. 绘制(Painting):将渲染树的每个节点转换为屏幕上的像素。
  6. 合成(Compositing):将多个图层合并成最终图像显示在屏幕上。

其中,布局对应的就是重排绘制对应的就是重绘


二、什么是重排(Reflow)?

重排(也称“回流”),指的是浏览器为了重新计算 DOM 元素的几何属性(如位置、大小),从而需要更新渲染树和布局的过程。

  • 本质:重新计算元素的几何信息

  • 影响范围

    • 局部重排:只影响部分 DOM 结构。例如,修改一个 div 的宽度,可能只导致该 div 及其后代元素重新布局。
    • 全局重排:影响整个页面的布局。例如,修改 <html> 根元素的字体大小,可能导致所有文本元素都需要重新计算尺寸和位置,从而引发全局重排。全局重排成本极高。
  • 触发重排的操作示例

    • 添加、删除、修改 DOM 节点。
    • 改变元素的几何属性:width, height, padding, margin, border, top, left 等。
    • 改变浏览器窗口大小(resize 事件)。
    • 读取某些会触发布局计算的属性(见下文)。
    • 访问 offsetTop, offsetLeft, offsetWidth, offsetHeight, scrollTop, scrollLeft, clientWidth, clientHeight, getComputedStyle() 等。注意:这些属性的读取本身就会强制浏览器立即执行重排以获取最新值,这被称为“强制同步布局”(Forced Synchronous Layout),是性能杀手。

三、什么是重绘(Repaint)?

重绘,指的是当元素的外观(如颜色、背景、边框样式等)发生变化,但几何属性没有改变时,浏览器需要重新绘制该元素的过程。

  • 本质:重新绘制元素的视觉外观

  • 影响范围:通常只影响元素本身及其视觉层叠相关的区域。

  • 成本:重绘的成本远低于重排,因为它跳过了计算布局的昂贵步骤。

  • 触发重绘的操作示例

    • 改变元素的颜色:color
    • 改变背景色或背景图片:background-color, background-image
    • 改变边框颜色或可见性:border-color, visibilityvisibility: hidden 会重绘,但元素仍占据空间)。
    • 改变文本样式:font-weight, text-decoration 等。
    • 注意display: none 会触发重排(因为它移除了元素,改变了布局),而 visibility: hidden 只触发重绘

四、关键区别与关系

特性 重排 (Reflow) 重绘 (Repaint)
触发原因 几何属性改变(位置、尺寸) 外观属性改变(颜色、背景等)
执行步骤 更新渲染树 → 布局 (Layout) → 绘制 (Painting) → 合成 更新渲染树 → 绘制 (Painting) → 合成
性能成本 非常高 相对较低
是否包含 必然包含重绘 不包含重排
影响范围 可能影响局部或全局布局 通常影响局部视觉

核心关系重排一定会导致重绘,但重绘不一定导致重排。

想象一下:你修改了一个盒子的宽度(重排),它的位置和大小都变了,那么它当然需要被重新画出来(重绘)。但如果你只是把盒子从红色改成蓝色(重绘),它的位置和大小没变,就不需要重新计算布局(无重排)。


五、如何减少重排和重绘?

  1. 批量修改 DOM

    • 使用 DocumentFragment 创建一个临时容器,在容器内完成所有 DOM 操作,然后一次性插入到 DOM 树中。
    • 或者先将元素 display: none(触发一次重排),进行多次修改,再恢复 display(再触发一次重排),避免中间过程的多次重排。
  2. 避免“强制同步布局”

    • 不要在修改样式后立即读取 offsetTop 等布局属性。将读写操作分开,先完成所有写操作,再统一读取。
    // ❌ 错误:反复触发重排
    for (let i = 0; i < items.length; i++) {
        items[i].style.width = items[i].offsetWidth + 10 + 'px'; // 读写交替
    }
    
    // ✅ 正确:先读取,再修改
    const widths = items.map(item => item.offsetWidth);
    requestAnimationFrame(() => {
        items.forEach((item, i) => {
            item.style.width = widths[i] + 10 + 'px';
        });
    });
  3. 使用 CSS 类名代替直接修改样式

    • 将多条 CSS 规则定义在一个类中,通过 classList.add/remove/toggle 来切换,而不是逐个修改 style 属性。
  4. 使用 transformopacity 实现动画

    • 这两个属性可以触发合成层(Compositing Layer),浏览器可以在独立的图层上进行 GPU 加速处理,通常只涉及合成(Compositing),既不重排也不重绘,性能最佳。
  5. 优化 CSS 选择器

    • 避免使用过于复杂或低效的 CSS 选择器,减少样式计算时间。

六、总结

重排和重绘是浏览器渲染性能的“双刃剑”。频繁的重排是导致页面卡顿、动画不流畅的罪魁祸首。作为前端开发者,我们需要深刻理解它们的原理和触发条件,并在实践中采取有效的策略来最小化它们的发生。通过合理使用 DocumentFragment、避免强制同步布局、利用 CSS 类和 transform/opacity 等技巧,我们可以显著提升应用的响应速度和用户体验。记住:能不重排就不重排,能不重绘就不重绘,这是性能优化的金科玉律。

相关文章

vuex和缓存(localStore)的主要区别

Vuex和缓存的主要区别在于它们的存储位置、‌数据类型、‌持久性、‌应用场景以及生命周期。‌ 存储位置:‌ Vuex数据存储在内存中,‌而缓存数据则存储在本地文件或内存中。‌具体来说,‌Vuex用于管理组...

CSS预处理器的优化与思考:从效率工具到工程化基石

一、引言:预处理器为何仍是前端工程的「刚需」?在原生CSS逐步支持变量(--var)、嵌套语法(CSS Nesting Level 3草案)的今天,有人质疑:“CSS预处理器是否即将退出历史舞台?” 但现实是,在中大型项目中,Sass、Less等工具依然是工程化的核心组件。它们解决的不仅是语法糖问题...

用Lottie做前端动画:从设计到落地的全流程实践

用Lottie做前端动画:从设计到落地的全流程实践

一、引言:为什么选择Lottie做动画?在前端开发中,实现复杂动画往往面临两大痛点: 手动编写CSS/JS动画代码繁琐:关键帧调试、性能优化耗时耗力,尤其是复杂交互动画 传统动画格式缺陷:GIF画质差、文件体积大;视频无法实现动态交互,且难以适配不同屏幕 Lottie的出现解决了这些...

【JS】什么是Document Fragment?

DocumentFragment 是 Web API 中的一个接口,表示一个没有父级的最小化文档对象。它被设计为一个轻量级的“文档片段”容器,可以用来存储一组节点,通常用于高效地进行 DOM 操作。 核心概念 虚拟容器:DocumentFragment 本身不是一个完整的文档,也不是实际 DOM...

【JS】Map和Object的区别

JavaScript 中的 Map 和 Object 都可以用来存储键值对,但它们在设计、性能和使用场景上有显著的区别。理解这些差异有助于在开发中做出更合适的选择。 1. 键的类型 Object: 键只能是字符串或Symbol。如果使用其他类型的值作为键(如数字、对象),它们会被自动转...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。