决战浏览器渲染:减少重绘(Repaint)与重排(Reflow)的性能优化策略
在现代Web开发中,流畅的用户体验是衡量应用质量的关键指标之一。用户与界面的每一次交互,背后都牵动着浏览器复杂而精密的渲染过程。当这个过程不够高效时,用户就会感受到卡顿、延迟,甚至页面“掉帧”。在众多影响渲染性能的因素中,重排(Reflow / Layout) 和 重绘(Repaint / Paint) 是两个核心且代价高昂的操作。理解它们、识别它们、并最终有效地减少它们,是前端性能优化攻坚战中的重要一役。
本文将带你深入浏览器渲染的幕后,剖析重排与重绘的原理、触发条件,并分享一系列经过实战检验的优化策略,助你打造如丝般顺滑的应用界面。
一、浏览器渲染流水线:重排与重绘的舞台
在我们深入探讨重排和重绘之前,先快速回顾一下浏览器将HTML、CSS和JavaScript转化为屏幕像素的大致流程:
- 解析(Parsing): 浏览器解析HTML文档,构建DOM树(Document Object Model)。同时解析CSS,构建CSSOM树(CSS Object Model)。
- 样式计算(Style Calculation): 结合DOM树和CSSOM树,计算出每个DOM节点最终应用的样式。
- 布局(Layout / Reflow): 根据计算好的样式,浏览器计算每个节点在屏幕上的精确位置和尺寸(几何信息)。这个过程也称为回流或重排。输出的是一个“布局树”或“渲染树”(Render Tree),只包含可见元素及其布局信息。
- 绘制(Paint / Repaint): 浏览器根据布局树和计算好的样式,将元素绘制成屏幕上的实际像素。这个过程涉及将元素的视觉内容(颜色、边框、阴影等)绘制到多个“绘制层”(Paint Layers)上。
- 合成(Compositing): 浏览器将多个绘制层按照正确的顺序合并,最终显示在屏幕上。对于利用GPU加速的特定CSS属性(如
transform,opacity),合成阶段可以直接在GPU上高效完成,绕过CPU的布局和绘制,从而显著提升性能。
重排(Reflow)发生在第3步(布局),重绘(Repaint)发生在第4步(绘制)。 它们是渲染流水线中与元素几何属性和视觉外观直接相关的关键环节。
二、理解“代价昂贵”:为何要关注重排与重绘?
1. 重排(Reflow / Layout) - 牵一发而动全身
什么是重排? 当DOM元素的几何属性(如宽度、高度、边距、填充、边框、位置、字体大小等)发生变化,或者DOM结构发生改变(添加、删除节点),导致浏览器需要重新计算元素及其子元素、甚至整个文档的布局时,这个过程就是重排。
为何代价昂贵?
- 计算密集: 需要确定所有相关节点的大小和位置,涉及大量的几何计算。
- 影响范围广: 一个元素的重排通常会影响其祖先节点、兄弟节点以及后续节点,甚至可能导致整个页面布局的重新计算。就像移动了书架上的一本书,可能需要调整旁边所有书的位置。
- 阻塞主线程: 重排是同步发生的,会阻塞JavaScript执行和用户交互,直到布局计算完成。频繁或大规模的重排是导致页面卡顿的主要元凶之一。
常见触发重排的操作:
- 添加或删除可见的DOM元素。
- 元素内容改变,影响了尺寸(如文本变化、图片大小变化)。
- 元素尺寸改变(修改
width,height,padding,margin,border等)。 - CSS 动画/过渡,如果改变了布局属性。
- 获取某些布局相关的DOM属性,如
offsetTop,offsetLeft,offsetWidth,offsetHeight,scrollTop,scrollLeft,scrollWidth,scrollHeight,clientTop,clientLeft,clientWidth,clientHeight,getComputedStyle()(在某些情况下)。这被称为强制同步布局(下面会详述)。 - 窗口
resize事件。 - 修改浏览器默认字体大小。
2. 重绘(Repaint / Paint) - 描绘视觉变化
什么是重绘? 当元素的视觉外观发生改变(如颜色、背景色、可见性visibility, outline等),但其布局(几何属性)没有变化时,浏览器会跳过布局阶段,直接进入绘制阶段,重新绘制该元素及其影响区域的像素。
为何也需关注? 虽然重绘的开销通常小于重排(因为它不涉及几何计算),但它仍然需要在CPU上进行绘制操作,并将结果传递给GPU。频繁或大面积的重绘同样会消耗资源,影响性能。
重要关系:重排必然导致重绘,但重绘不一定需要重排。 改变元素尺寸(重排)后,其外观自然也需要重新绘制。但只改变背景颜色(重绘),通常不需要重新计算布局。
常见触发重绘的操作:
- 改变
color,background-color,outline,visibility,text-decoration等不影响布局的样式。 - 所有触发重排的操作,最终也会触发重绘。
三、性能杀手:强制同步布局(Forced Synchronous Layout)
浏览器为了优化性能,通常会将多次可能触发重排/重绘的操作“暂存”起来,然后在某个时间点(通常是当前JavaScript任务结束前或下一帧渲染前)进行一次批处理。这被称为异步布局/渲染。
然而,如果在修改了可能导致重排的样式之后,立即在JavaScript中读取需要依赖最新布局信息的属性(如offsetHeight, clientWidth等),浏览器为了返回准确的值,就不得不立即执行布局(Reflow)操作,强制清空“暂存”队列并进行计算。这就是强制同步布局。
糟糕的模式(读写循环):
const element = document.getElementById('myElement');// 假设我们想让元素高度等于其宽度
// 每次循环都 读取 -> 写入 -> 读取 -> 写入...
for (let i = 0; i < 100; i++) {// 写入:改变可能影响布局的样式 (假设改变宽度会影响高度,或者反之)element.style.width = (element.offsetWidth + 1) + 'px'; // 读取 offsetWidth -> 强制布局// 读取:又读取一个布局属性 (即使这里没用,读取本身就会触发)console.log(element.offsetHeight); // 再次强制布局 (如果上次写入改变了高度)
}
// 结果:可能触发了 100 次甚至更多的强制同步布局!非常低效!
为何是杀手? 它打破了浏览器的优化机制,将本可以批量处理的布局计算变成了多次同步、阻塞的操作,导致JavaScript执行时间大大延长,页面卡顿。
四、实战优化策略:驯服重排与重绘
理解了原理和痛点,接下来就是如何行动。以下是一些核心的优化策略:
1. 减少DOM操作的频率和范围
- 批量处理DOM更改: 如果需要多次修改DOM(添加、删除、更新),尽量一次性完成。
- 使用
DocumentFragment: 创建一个文档片段作为临时容器,在片段中进行所有DOM操作,最后将整个片段一次性插入到主DOM中。这只会触发一次重排。
const fragment = document.createDocumentFragment(); for (let i = 0; i < 10; i++) {const newItem = document.createElement('li');newItem.textContent = `Item ${i}`;fragment.appendChild(newItem); } document.getElementById('myList').appendChild(fragment); // 只在这里触发一次重排/重绘- 隐藏元素再操作: 对于已在DOM中的元素,可以先将其
display: none(触发一次重排),然后进行多次修改,最后再恢复显示(又一次重排)。总共两次重排,优于每次修改都触发。
- 使用
- 拷贝操作: 如果需要对某个节点做大量修改,可以先克隆它 (
cloneNode()),在克隆节点上操作,然后替换掉原始节点。
2. 优化CSS,拥抱合成层(Compositor Layers)
- 避免使用Table布局: Table布局计算复杂,一个小改动可能导致整个表格重排。优先使用Flexbox或Grid布局。
- 精简CSS选择器: 虽然现代浏览器在这方面优化得很好,但过于复杂的选择器仍可能轻微增加样式计算时间。保持选择器简洁高效总没错。
- 使用
transform和opacity进行动画/过渡: 这是最重要的CSS优化技巧之一。transform(位移、旋转、缩放) 和opacity(透明度) 的改变,通常可以被浏览器优化到合成层上处理。这意味着它们不会触发重排或重绘,而是直接由GPU处理图层的变换和混合,效率极高。/* 不好的方式:使用 top/left 触发重排 */ .animate-bad {position: absolute;transition: top 0.3s, left 0.3s; } .animate-bad:hover {top: 50px;left: 50px; }/* 好的方式:使用 transform 触发合成 */ .animate-good {transition: transform 0.3s; } .animate-good:hover {transform: translate(50px, 50px); } - 使用
will-change属性(谨慎使用): 提前告知浏览器某个元素的某些属性可能会发生变化,让浏览器有机会进行预优化,比如提前将其提升到独立的合成层。
注意: 不要滥用.element-about-to-animate {will-change: transform, opacity; }will-change。过度使用会消耗更多内存,因为它强制浏览器创建合成层。只在确实需要优化动画且效果明显的元素上使用,并在动画结束后移除该属性(如果可能)。
3. 优化JavaScript执行
- 避免强制同步布局: 这是必须遵守的黄金法则。遵循“读写分离”原则:
- 批量读取: 在进行任何写操作之前,将所有需要读取的布局信息(如
offsetWidth,scrollTop等)一次性读出并存储在变量中。 - 批量写入: 然后,根据读取到的值,进行所有DOM或样式的修改。
// 好的模式:读写分离 const element = document.getElementById('myElement'); const currentWidth = element.offsetWidth; // 先读取 const currentHeight = element.offsetHeight; // 再读取// 然后进行所有写操作 element.style.width = (currentWidth + 10) + 'px'; element.style.height = (currentHeight * 1.1) + 'px'; element.style.color = 'red'; // 这个是重绘,但也一起做了 // 这样,即使修改了多个样式,浏览器也更有可能将它们合并为一次重排/重绘 - 批量读取: 在进行任何写操作之前,将所有需要读取的布局信息(如
- 使用
requestAnimationFrame(rAF) 执行动画: 对于JavaScript驱动的动画,应使用rAF而不是setTimeout或setInterval。rAF能确保你的动画更新函数在浏览器下一次重绘之前执行,与浏览器的刷新率同步,避免不必要的计算和渲染,使动画更流畅。function stepAnimation() {// 更新动画状态...updateElementStyle();if (animationIsRunning) {requestAnimationFrame(stepAnimation);} } requestAnimationFrame(stepAnimation); // 启动动画 - 对高频事件(
resize,scroll,input等)进行节流(Throttling)或防抖(Debouncing): 这些事件可能在短时间内触发非常多次,如果事件处理函数中包含重排或重绘操作,会造成严重性能问题。使用节流(保证一定时间间隔内最多执行一次)或防抖(事件停止触发后一段时间再执行)来限制处理函数的执行频率。
4. 利用工具诊断
- Chrome DevTools - Performance 面板:
- 录制一段时间的操作,查看火焰图 (Flame Chart) 中的
Layout(紫色) 和Painting(绿色) 部分。长条表示耗时较长的操作,是优化的重点。 - 留意
Recalculate Style(紫色) 和Update Layer Tree(紫色) 事件。 - 寻找红色三角警告,通常表示发生了强制同步布局(Forced Reflow)。点击可以查看调用栈,定位问题代码。
- 录制一段时间的操作,查看火焰图 (Flame Chart) 中的
- Chrome DevTools - Rendering (渲染) 标签页 (通常在
More tools里):- 勾选
Paint flashing:页面上发生重绘的区域会以绿色高亮显示,可以直观看到哪些操作触发了哪些区域的重绘。 - 勾选
Layout Shift Regions:页面上发生布局变化的区域会以蓝色高亮显示。这对于调试累积布局偏移(CLS - Cumulative Layout Shift)指标尤其有用。
- 勾选
五、总结:性能优化的持续战争
浏览器渲染是一个精密的过程,重排和重绘是其中不可避免但可以被有效管理的环节。优化它们并非一劳永逸,而是在开发过程中需要持续关注的实践。
核心要点回顾:
- 理解重排(布局计算)和重绘(像素绘制)的触发条件和性能代价。
- 最小化DOM操作,利用
DocumentFragment或隐藏元素进行批量修改。 - **优先使用
transform和opacity**进行动画和视觉变换,利用合成层优势。谨慎使用will-change。 - 坚决避免强制同步布局,遵循“读写分离”原则。
- 使用
requestAnimationFrame处理JS动画,对高频事件进行节流/防抖。 - 利用DevTools等工具进行诊断和验证优化效果。
通过将这些策略融入日常开发习惯,你就能更从容地应对这场“决战”,为用户打造出更加流畅、响应迅速的Web体验。
相关文章:
决战浏览器渲染:减少重绘(Repaint)与重排(Reflow)的性能优化策略
在现代Web开发中,流畅的用户体验是衡量应用质量的关键指标之一。用户与界面的每一次交互,背后都牵动着浏览器复杂而精密的渲染过程。当这个过程不够高效时,用户就会感受到卡顿、延迟,甚至页面“掉帧”。在众多影响渲染性能的因素中…...
好数对的数目
题目描述 给你一个整数数组 nums。 如果一组数字 (i, j) 满足 nums[i] nums[j] 且 i < j,就可以认为这是一组 好数对。 返回 好数对 的数目。 示例 示例 1: 输入:nums [1,2,3,1,1,3] 输出:4 解释: 有 4 组好…...
C++ STL编程-vector概念、对象创建
vector 概念:是常见的一种容器,被称为“柔性数组”。 在vector中,front()是数组中的第一个元素,back()是数组的最后一个元素。begin()是是指向第一个元素,end()是指向back()的后一个元素 vector的对象创建࿰…...
RUI电视桌面中文版:下载安装教程及桌面固件包获取全攻略
在智能电视的使用过程中,一款出色的桌面系统能极大提升用户体验,RUI电视桌面中文版就是这样一个不错的选择。下面为大家详细介绍RUI电视桌面中文版的下载安装教程以及桌面固件包的获取方法。 一、桌面固件包获取 首先是获取桌面固件包。可以通过RUI官方…...
OpenAI 34页最佳构建Agent实践
penAI发布O4,也发布34页最佳构建Agent实践,值得阅读。 什么是Agent? 传统软件使用户能够简化和自动化工作流程,而代理能够以高度独立的方式代表用户执行相同的工作流程。 代理是能够独立地代表您完成任务的系统。 工作流程是必…...
HOOPS Exchange 与HOOPS Communicator集成:打造工业3D可视化新标杆!
一、概述 在工业3D开发、BIM建筑、数字孪生和仿真分析等高端应用场景中,数据格式复杂、模型体量庞大、实时交互体验要求高,一直是困扰开发者的难题。Tech Soft 3D旗下的HOOPS Exchange和HOOPS Communicator,正是解决这类问题的黄金搭档。二者…...
C#进阶学习(六)单向链表和双向链表,循环链表(下)循环链表
目录 📊 链表三剑客:特性全景对比表 一、循环链表节点类 二、循环链表的整体设计框架 三、循环列表中的重要方法: (1)头插法,在头结点前面插入新的节点 (2)尾插法实现插入元素…...
后端程序员工作复盘(一)
1、工作不是为了解决问题,而是为了生活目标。 2、不能当救火队员,要提前预防问题的产生、避免问题的出现。 3、后端表设计和接口设计,要考虑到扩展性,要灵活。无论页面如何变动,后端的改动量都最小,要以不…...
禅道部署进阶指南:从搭建到高可用,全程打怪升级!
禅道在生产环境中的更专业部署方案,包括 Linux 服务器部署、Docker 安装方案、性能优化、安全建议和常见企业级集成方式,适合团队使用或对稳定性、安全性有较高要求的项目。 ✅ 一、企业级部署方案(适合 Linux 环境) 🖥 环境要求 操作系统:CentOS 7+/Ubuntu 18+(推荐)…...
文章记单词 | 第36篇(六级)
一,单词释义 wit [wɪt] n. 智慧;才智;机智;风趣的人dreadful [ˈdredfl] adj. 糟糕透顶的;可怕的;令人畏惧的innocent [ˈɪnəsnt] adj. 无辜的;天真无邪的;无罪的;无…...
Unity使用Newtonsoft.Json本地化存档
我是标题 1.依赖包2.原理:3.代码4.可用优化5.数据加密 1.依赖包 Newtonsoft请在PacakgeManager处下载。 参考:打工人小棋 2.原理: 把要存储的对象数据等使用JsonConvert.SerializeObject(object T)进行序列化为字符串,并且通过…...
Java研学-MybatisPlus(一)
一 概述 MyBatis-Plus(简称 MP)是一款基于 MyBatis 的增强工具,旨在简化开发、提高效率。它在保留 MyBatis 所有特性的基础上,提供了丰富的功能,减少了大量模板代码的编写。 1 核心特性: ① 无侵入增强&am…...
2025年03月中国电子学会青少年软件编程(Python)等级考试试卷(六级)真题
青少年软件编程(Python)等级考试试卷(六级) 分数:100 题数:38 答案解析:https://blog.csdn.net/qq_33897084/article/details/147341458 一、单选题(共25题,共50分) 1. 在tkinter的…...
OpenVINO怎么用
目录 OpenVINO 简介 主要组件 安装 OpenVINO 使用 OpenVINO 的基本步骤 OpenVINO 简介 OpenVINO(Open Visual Inference and Neural Network Optimization)是英特尔推出的一个开源工具包,旨在帮助开发者在英特尔硬件平台上高效部署深度学…...
欧拉服务器操作系统安装MySQL
1. 安装MySQL服务器 1. 更新仓库缓存 sudo dnf makecache2. 安装MySQL sudo dnf install mysql-server2. 初始化数据库 sudo mysqld --initialize --usermysql3. 启动数据库服务 # 启动服务 sudo systemctl start mysqld# 设置开机自启 sudo systemctl enable mysql…...
【零基础】基于 MATLAB + Gurobi + YALMIP 的优化建模与求解全流程指南
MATLAB Gurobi YALMIP 综合优化教程(进阶) 本教程系统介绍如何在 MATLAB 环境中使用 YALMIP 建模,并通过 Gurobi 求解器高效求解线性、整数及非线性优化问题。适用于工程、运营研究、能源系统等领域的高级优化建模需求。 一、工具概览 1.…...
Python 浮点数运算之谜:深入解析round(0.675, 2)等输出异常
一、问题背景:当浮点数运算遇见 “反直觉” 结果 在 Python 开发中,以下代码输出常让开发者困惑: print(round(0.675, 2)) # 预期0.67,实际0.68||预期0.68,实际0.67 print(0.1 0.2) # 预期0.3&…...
【C#】Html转Pdf,Spire和iTextSharp结合,.net framework 4.8
🌹欢迎来到《小5讲堂》🌹 🌹这是《C#》系列文章,每篇文章将以博主理解的角度展开讲解。🌹 🌹温馨提示:博主能力有限,理解水平有限,若有不对之处望指正!&#…...
极狐GitLab 注册限制如何设置?
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 注册限制 (BASIC SELF) 您可以对注册实施以下限制: 禁用新注册。新注册需要管理员批准。需要用户电子邮件确认。…...
利用大模型实现地理领域文档中英文自动化翻译
一、 背景描述 在跨国性企业日常经营过程中,经常会遇到专业性较强的文档翻译的需求,例如法律文书、商务合同、技术文档等;以往遇到此类场景,企业内部往往需要指派专人投入数小时甚至数天来整理和翻译,效率低下&#x…...
SGFormer:卫星-地面融合 3D 语义场景补全
论文介绍 题目:SGFormer: Satellite-Ground Fusion for 3D Semantic Scene Completion 会议:IEEE / CVF Computer Vision and Pattern Recognition Conference 论文:https://www.arxiv.org/abs/2503.16825 代码:https://githu…...
Trinity三位一体开源程序是可解释的 AI 分析工具和 3D 可视化
一、软件介绍 文末提供源码和程序下载学习 Trinity三位一体开源程序是可解释的 AI 分析工具和 3D 可视化。Trinity 提供性能分析和 XAI 工具,非常适合深度学习系统或其他执行复杂分类或解码的模型。 二、软件作用和特征 Trinity 通过结合具有超维感知能力的不同交…...
城市街拍暗色电影胶片风格Lr调色教程,手机滤镜PS+Lightroom预设下载!
调色介绍 城市街拍暗色电影胶片风格 Lr 调色,是借助 Adobe Lightroom 软件,为城市街拍的人像或场景照片赋予独特视觉风格的后期处理方式。旨在模拟电影胶片质感,营造出充满故事感与艺术感的暗色氛围,让照片仿佛截取于某部充满张力…...
【家政平台开发(55)】家政平台数据生命线:备份与恢复策略全解析
本【家政平台开发】专栏聚焦家政平台从 0 到 1 的全流程打造。从前期需求分析,剖析家政行业现状、挖掘用户需求与梳理功能要点,到系统设计阶段的架构选型、数据库构建,再到开发阶段各模块逐一实现。涵盖移动与 PC 端设计、接口开发及性能优化,测试阶段多维度保障平台质量,…...
加密和解密(大语言模型)
看到很多对matlab的p文件加密方案感兴趣的。网络上技术资料比较少,所以,我让大语言模型提供一些概论性质的东西,转发出来自娱自乐。期望了解p文件加密的复杂度,而不是一定要尝试挑战加密算法。 但根据大语言模型提供的材料&#…...
双轮驱动能源革命:能源互联网与分布式能源赋能工厂能效跃迁
在全球能源结构深度转型与“双碳”目标的双重驱动下,工厂作为能源消耗的主力军,正站在节能变革的关键节点。能源互联网与分布式能源技术的融合发展,为工厂节能开辟了全新路径。塔能科技凭借前沿技术与创新实践,深度探索能源协同优…...
React 更新 state 中的数组
更新 state 中的数组 数组是另外一种可以存储在 state 中的 JavaScript 对象,它虽然是可变的,但是却应该被视为不可变。同对象一样,当你想要更新存储于 state 中的数组时,你需要创建一个新的数组(或者创建一份已有数组…...
ubantu18.04HDFS编程实践(Hadoop3.1.3)
说明:本文图片较多,耐心等待加载。(建议用电脑) 注意所有打开的文件都要记得保存。 第一步:准备工作 本文是在之前Hadoop搭建完集群环境后继续进行的,因此需要读者完成我之前教程的所有操作。 第二步&am…...
Spring Boot资源耗尽问题排查与优化
Spring Boot服务运行一段时间后新请求无法处理的问题。服务没有挂掉,也没有异常日志。思考可能是一些资源耗尽或阻塞的问题。 思考分析 首先,资源耗尽可能涉及线程池、数据库连接、内存、文件句柄或网络连接等。常见的如线程池配置不当,导致…...
优化WAV音频文件
优化 WAV 音频文件通常涉及 减小文件体积、提升音质 或 适配特定用途(如流媒体、广播等)。以下是分场景的优化方法,涵盖工具和操作步骤: 一、减小文件体积(无损/有损压缩) 1. 无损压缩 转换格式࿱…...
