ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

JavaScript / 浏览器渲染:什么时候发生重排和重绘?

2026/9/28 20:55:29 拓冰建站 浏览量
JavaScript / 浏览器渲染:什么时候发生重排和重绘? 一、面试题什么是重排Reflow什么时候会发生核心思路一句话重排本质是重新计算页面元素的几何布局凡是可能影响元素位置、尺寸或布局关系的变化都可能触发重排。解决方案流程图DOM / CSSOM 发生变化 ↓ 样式计算 ↓ 是否影响几何布局 ┌────┴────┐ ↓ ↓ 是 否 ↓ ↓ 重新计算布局 不重排 ↓ 重新计算受影响的布局 ↓ 后续通常还要重新绘制底层实现原理浏览器渲染过程中可以简化理解为HTML ↓ DOM Tree ↓ CSSOM ↓ 样式计算 ↓ Layout Tree / 布局信息 ↓ 布局计算位置、尺寸、盒模型等 ↓ Paint绘制 ↓ Rasterization光栅化 ↓ Composite合成重排发生在“布局”阶段。布局阶段主要计算元素在哪里 元素有多宽 元素有多高 元素之间如何排列 文本换行后占多少空间 父元素尺寸是否需要重新计算因此下面这些变化通常会触发重排width height padding margin border-width font-size line-height position top / right / bottom / left display flex / grid 相关布局属性但要注意不是“修改任何 CSS 属性都一定重排”真正的判断标准是这个变化是否影响布局几何信息。例如/* 通常不会改变布局 */color background-color outline-color box-shadow而/* 会改变几何布局 */width height padding margin font-size二、面试题修改布局属性会立即发生重排吗核心思路一句话修改布局属性后浏览器通常不会立即重排而是先把修改积累起来在合适的渲染时机批量处理。结构化逻辑JavaScript ↓ 修改 width ↓ 修改 height ↓ 修改 padding ↓ 修改 margin ↓ 浏览器先记录这些变化 ↓ 同步 JavaScript 执行结束 ↓ 下一次渲染机会 ↓ 统一进行布局计算例如constboxdocument.querySelector(.box);// 连续修改多个会影响布局的属性box.style.width500px;box.style.height300px;box.style.padding20px;box.style.margin10px;// 一般不会因为这 4 次修改就立即进行 4 次完整布局。// 浏览器通常会把这些修改合并在后续渲染阶段统一处理。主要矛盾浏览器会尽可能批量合并布局修改避免每修改一次属性就重新计算整个布局。所以不能简单说“修改一次布局属性 立即重排一次”。更准确的说法是布局修改通常会让布局变“脏”浏览器会延迟布局计算但如果之后同步读取依赖最新布局的数据就可能被迫提前执行布局。三、面试题读取哪些布局信息会强制触发重排核心思路一句话如果浏览器发现你要读取最新的几何布局信息而当前布局还是脏的就必须先完成布局计算再把最新结果返回给 JavaScript。典型 APIelement.offsetWidth element.offsetHeight element.offsetTop element.offsetLeft element.clientWidth element.clientHeight element.scrollWidth element.scrollHeight element.scrollTop element.scrollLeft element.getBoundingClientRect()window.getComputedStyle(element)其中尤其常见constboxdocument.querySelector(.box);box.style.width500px;// 这里需要最新的布局结果console.log(box.offsetWidth);浏览器大致需要修改 width ↓ 布局数据变脏 ↓ 读取 offsetWidth ↓ “我现在必须告诉你最新宽度” ↓ 强制完成布局计算 ↓ 返回最新 offsetWidth这就是常说的强制同步布局Forced Synchronous Layout为什么必须同步假设box.style.width500px;console.log(box.offsetWidth);如果浏览器不重新计算布局那么offsetWidth可能只能返回修改前的旧布局结果。所以CSS/DOM 修改 ↓ 布局数据变脏 ↓ 读取几何信息 ↓ 不能继续拿旧数据 ↓ 强制更新布局 ↓ 返回最新数据四、面试题为什么“读写交替”容易造成性能问题核心思路一句话连续写可以批量处理但“写 → 读 → 写 → 读”会不断迫使浏览器同步完成布局。典型问题代码constboxesdocument.querySelectorAll(.box);boxes.forEach((box){// 写让布局变脏box.style.width500px;// 读要求浏览器立即提供最新布局console.log(box.offsetWidth);});可能形成写 ↓ 读 → 强制布局 ↓ 写 ↓ 读 → 强制布局 ↓ 写 ↓ 读 → 强制布局这就可能产生大量同步布局计算。更好的方式读写分离constboxesdocument.querySelectorAll(.box);// 第一阶段集中读取constwidthsArray.from(boxes,(box)box.offsetWidth);// 第二阶段集中修改boxes.forEach((box){box.style.width${box.offsetWidth100}px;});更严谨地说如果修改计算依赖之前读取的数据constboxesdocument.querySelectorAll(.box);// 1. 先统一读取constwidthsArray.from(boxes,(box)box.offsetWidth);// 2. 再统一写入boxes.forEach((box,index){box.style.width${widths[index]100}px;});结构就是❌ 写 → 读 → 写 → 读 → 写 → 读 ✅ 读 → 读 → 读 → 读 ↓ 统一计算 ↓ 写 → 写 → 写 → 写五、面试题为什么修改width: 0后再修改width: 100px有时候看不到过渡动画核心思路一句话因为连续同步修改可能在真正渲染前被浏览器合并浏览器最终只看到width: 100px没有看到一个独立的0px渲染状态。例如box.style.width0px;box.style.transitionwidth 0.5s;box.style.width100px;浏览器可能在真正绘制之前才统一处理这些修改。逻辑上更接近width 0 ↓ width 100 ↓ 真正渲染 ↓ 浏览器只看到最终状态因此没有形成0px ↓ 一次独立渲染 ↓ 100px ↓ transition六、如何强制浏览器先完成布局再触发动画传统方案读取一个几何属性constboxdocument.querySelector(.box);box.style.transitionwidth 0.5s;box.style.width0px;// 强制浏览器同步完成布局voidbox.offsetWidth;// 此时浏览器已经确认了 width: 0px 的布局状态box.style.width100px;为什么void box.offsetWidth有效重点不是void。真正起作用的是box.offsetWidth因为它需要最新的布局信息所以可能迫使浏览器修改 width 0 ↓ 布局变脏 ↓ 读取 offsetWidth ↓ 强制同步布局 ↓ 确认 width 0 的布局状态 ↓ 修改 width 100 ↓ 后续渲染 ↓ 触发 transitionvoid只是voidbox.offsetWidth;用来明确表示“我只关心读取产生的副作用不关心返回值。”更推荐的现代写法如果目的是制作动画优先考虑 CSS 动画、Web Animations API 或基于transform的动画而不是主动制造强制同步布局。例如.box{width:100px;transition:transform 0.5s;}.box.hidden{transform:scaleX(0);transform-origin:left;}constboxdocument.querySelector(.box);// 通过 class 控制动画状态box.classList.add(hidden);相比频繁修改box.style.width...transform通常更适合动画因为它通常不会直接改变普通文档流中的布局。但这不是绝对规则。最终是否产生布局、绘制或合成开销需要结合具体属性和浏览器实现分析。七、面试题什么是重绘Repaint什么时候发生核心思路一句话重绘是布局不变但元素的可见视觉效果发生变化需要重新绘制像素。典型属性color background-color box-shadow border-color outline-color例如constboxdocument.querySelector(.box);// 不改变盒子的尺寸和位置box.style.backgroundColorred;通常流程修改 background-color ↓ 布局没有变化 ↓ 不需要重新计算位置和尺寸 ↓ 重新绘制八、重排和重绘是什么关系核心思路一句话重排影响布局重绘影响视觉重排通常会导致后续重新绘制但重绘不一定需要重排。可以直接记重排 ↓ 布局发生变化 ↓ 通常需要重新绘制而重绘 ↓ 视觉表现变化 ↓ 布局没有变化 ↓ 不需要重排因此重排 ⊃ 后续绘制工作 重绘 ≠ 一定重排举例// 通常只需要重绘box.style.colorred;// 通常需要重排并进一步触发绘制box.style.width500px;九、面试题改变字体大小和字体类型会不会重排核心思路一句话关键不是“字体属于文字样式”而是字体变化是否改变文本的几何尺寸和布局。font-size通常会触发重排font-size:20px;因为字体大小变化可能导致文字尺寸变化 ↓ 行盒尺寸变化 ↓ 文本换行变化 ↓ 父元素高度变化 ↓ 后续元素位置变化 ↓ 重新布局font-family也可能触发重排。例如font-family:Arial;改成font-family:Times New Roman;即使font-size:16px;保持不变不同字体的字符宽度、字形度量可能不同因此可能导致文字宽度变化 ↓ 换行位置变化 ↓ 文本区域高度变化 ↓ 布局变化所以不能简单记“文字颜色重绘字体相关全部重绘。”正确判断方式始终是这个属性是否可能影响几何布局 ↓ 是 → 可能重排 否 → 通常只需要重绘十、面试题outline和border有什么区别为什么outline通常不会触发重排核心思路一句话border属于盒模型的一部分会影响布局尺寸outline不参与正常布局因此改变outline通常不会导致重排。例如.box{width:200px;height:100px;/* 会参与盒模型计算 */border:10px solid black;/* 不参与正常布局 */outline:10px solid red;}核心区别border ↓ 盒模型的一部分 ↓ 可能改变元素布局尺寸 ↓ 可能触发重排 outline ↓ 不参与正常布局 ↓ 不会改变元素占据的布局空间 ↓ 通常只需要重绘实际使用场景如果只是为了调试元素边界*{outline:1px solid red;}通常比*{border:1px solid red;}更适合调试因为outline不会改变元素原本的布局。十一、面试题DOM 节点和布局树节点是一一对应的吗核心思路一句话不是一一对应DOM 是文档结构布局树描述参与布局的盒子两者抽象层级不同。架构关系HTML ↓ DOM Tree ↓ 样式计算 ↓ Layout Tree ↓ 布局盒子 ↓ Paint不能简单理解成一个 DOM 节点 ↓ 一个布局节点实际可能出现DOM 节点 ├── 不产生布局盒子 ├── 产生一个布局盒子 └── 产生多个相关布局盒子情况一display: nonedivclassboxHello/div.box{display:none;}这个 DOM 节点仍然存在DOM ↓ 存在但display: none ↓ 不参与正常布局 ↓ 不会产生对应的布局盒子所以DOM 存在 ≠ 一定存在对应布局盒子。注意visibility: hidden与display: none不同。visibility: hidden通常仍然占据布局空间只是不绘制可见内容。情况二伪元素.box::before{content:Hello;}::before并不是普通的 DOM 子节点但它可以参与布局和绘制。因此DOM Tree ↓ 样式生成的伪元素 ↓ 布局 / 绘制这进一步说明布局结构并不等于 DOM 树。情况三匿名盒子例如divclasscontainerHello World/div浏览器需要根据 CSS 盒模型规则处理文本内容。文本并不会因为没有显式写spanHello World/span就直接违反布局规则。浏览器会根据 CSS 规则生成相应的匿名盒子来参与布局。所以布局树中可能存在DOM ↓ div ↓ 匿名布局盒子 ↓ 文本布局这也是 DOM 和布局树不一一对应的重要原因。十二、面试题重排和重绘应该如何系统判断核心思路一句话不要死记 CSS 属性先判断“布局有没有变”再判断“视觉有没有变”。判断模型发生 DOM / CSS / 样式变化 ↓ 是否影响布局 / \ 是 否 ↓ ↓ 重排 布局不变 ↓ ↓ 通常还需要 是否影响视觉 重新绘制 / \ 是 否 ↓ ↓ 重绘 无需绘制常见分类操作通常结果核心原因width重排 后续绘制几何尺寸变化height重排 后续绘制几何尺寸变化margin重排 后续绘制布局关系变化padding重排 后续绘制盒模型变化font-size通常重排 后续绘制文本几何尺寸可能变化font-family可能重排 后续绘制字体度量可能变化color重绘视觉变化通常不影响布局background-color重绘视觉变化box-shadow重绘视觉变化outline通常重绘不参与正常布局transform通常不触发普通布局通常由后续绘制/合成处理opacity通常不触发普通布局不改变几何信息这里的“通常”非常重要。浏览器实际渲染成本取决于具体属性、元素状态、布局依赖关系以及浏览器实现不能把所有 CSS 属性机械地贴上“重排/重绘”标签。十三、性能优化如何减少重排和重绘核心思路一句话核心不是“完全避免重排”而是减少不必要的布局计算并避免强制同步布局。优化流程发现页面卡顿 ↓ Chrome Performance 分析 ↓ 定位 Layout / Paint / Composite ↓ 检查 JS 是否存在 “写 → 读 → 写 → 读” ↓ 读写分离 ↓ 批量修改 DOM / CSS ↓ 动画优先考虑 transform / opacity ↓ 减少频繁布局变化 ↓ 重新测试方案一批量修改不推荐for(constitemofitems){item.style.width200px;console.log(item.offsetWidth);}推荐// 先完成所有读取constwidthsArray.from(items,(item)item.offsetWidth);// 再统一修改items.forEach((item,index){item.style.width${widths[index]20}px;});方案二避免频繁修改布局属性做动画不推荐box.style.left${x}px;box.style.top${y}px;更适合动画的方式box.style.transformtranslate(${x}px,${y}px);因为transform通常可以避免反复触发普通文档布局。方案三一次性修改 class不要box.style.width300px;box.style.height200px;box.style.padding20px;box.style.backgroundColorred;可以考虑box.classList.add(active);.active{width:300px;height:200px;padding:20px;background-color:red;}这样可以让样式变化更加集中也更容易维护。十四、主要矛盾和次要矛盾主要矛盾重排的核心问题是布局几何信息被改变或者 JavaScript 强制要求浏览器立即提供最新布局。也就是两个重点① 改布局 → 布局变脏 ② 读布局 → 必须得到最新结果 ↓ 可能强制同步布局次要矛盾DOM 与布局树不是一一对应 伪元素 匿名盒子 display: none font-family / font-size outline 与 border 动画实现方式这些知识重要但都是围绕一个核心问题展开浏览器什么时候必须重新计算布局以及什么时候只需要重新绘制。十五、最终满分答案面试题什么时候发生重排和重绘如何优化核心思路重排解决“布局怎么摆”重绘解决“画成什么样”真正需要重点关注的是布局变化以及 JavaScript 的“读写交替”导致的强制同步布局。1. 重排重排就是重新计算元素的位置、尺寸以及布局关系。当修改width、height、margin、padding、font-size等可能影响几何布局的属性时浏览器需要重新计算布局。修改 DOM / CSS ↓ 样式计算 ↓ 影响几何布局 ↓ 重新布局 ↓ 通常还要重新绘制浏览器通常会把连续的布局修改合并处理并不会每修改一次属性就立即重排。2. 读取布局信息为什么可能强制重排如果布局已经被修改但还没有重新计算此时读取offsetWidth offsetHeight clientWidth clientHeight scrollWidth scrollHeightgetBoundingClientRect()等几何信息时浏览器必须保证返回最新结果因此可能先强制完成布局。box.style.width500px;// 读取最新布局可能触发强制同步布局console.log(box.offsetWidth);所以性能优化中要避免写 → 读 → 写 → 读尽量改成集中读取 ↓ 集中计算 ↓ 集中写入3. 重绘重绘是布局没有变化但元素的可见视觉效果发生变化需要重新绘制。例如box.style.colorred;box.style.backgroundColorblue;box.style.boxShadow0 0 10px gray;这些通常不会改变元素的布局但需要重新绘制。4. 两者关系重排 ↓ 布局发生变化 ↓ 通常需要后续绘制 重绘 ↓ 视觉发生变化 ↓ 不一定需要重排因此重排的成本通常比单纯重绘更值得重点关注但实际性能还要结合具体页面和浏览器渲染过程分析。5. 一个重要边界DOM 节点和布局树节点不是一一对应display: none ↓ DOM 节点存在 ↓ 不参与正常布局 伪元素 / 匿名盒子 ↓ 可能参与布局 ↓ 但不是普通 DOM 节点所以不能简单认为一个 DOM 节点就对应一个布局节点。6. 最后一句话判断重排还是重绘不要死记属性核心看它是否影响布局影响几何布局就是重排不影响布局但改变可见效果通常是重绘而“修改布局后立即读取布局信息”容易触发强制同步布局是前端性能优化中特别需要避免的情况。这版面试时建议重点记住 3 个关键词布局变化、强制同步布局、读写分离。