浏览器渲染页面的过程,以及重绘和重排

本文详细介绍了浏览器如何解析HTML和CSS代码以创建DOM树和渲染树的过程,并解释了重绘和重排的概念及其对网页性能的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浏览器的渲染过程


1,浏览器解析html源码,然后创建一个 DOM树。
在DOM树中,每一个HTML标签都有一个对应的节点,并且每一个文本也都会有一个对应的文本节点。
DOM树的根节点就是 <html>。

2,浏览器解析CSS代码,计算出最终的样式数据。
对CSS代码中非法的语法她会直接忽略掉。
解析CSS的时候会按照如下顺序来定义优先级(从低到高):浏览器默认设置,用户设置,外联样式,内部样式,内联样式。

3,构建出DOM树,并且计算出样式数据后,下一步就是构建一个 渲染树(rendering tree)。
渲染树和DOM树有点像,但是是有区别的。DOM树完全和html标签一一对应,但是渲染树会忽略掉不需要渲染的元素,比如head、display:none的元素等。
而且一大段文本中的每一个行在渲染树中都是独立的一个节点。
渲染树中的每一个节点都存储有对应的css属性。

4,一旦渲染树创建好了,浏览器就可以根据渲染树直接把页面绘制到屏幕上。


一个渲染过程的例子


例如有下面这样一段HTML代码:
<html>
<head>
  <title>Beautiful page</title>
</head>
<body>
    
  <p>
    Once upon a time there was 
    a looong paragraph...
  </p>
  
  <div style="display: none">
    Secret message
  </div>
    
  <div><img src="..." /></div>
  ...
 
</body>
</html>

那么DOM树是完全和HTML标签一一对应的,如下所示:
html
    head
        title
    body
        p
            [text node]
                
        div 
            [text node]
                
        div
            img
                
        ...

而渲染树就不同了,她只有哪些需要绘制出来的元素,所以head 以及被隐藏的div都不会出现在渲染树中。
root (RenderView)
    body
        p
            line 1
            line 2
            line 3
            ...
            
        div
            img
            
        ...

重绘和重排(repaints and reflows)

每个页面至少在初始化的时候会有一次重排操作。任何对渲染树的修改都有可能会导致下面两种操作:
1,重排
就是渲染树的一部分必须要更新 并且节点的尺寸发生了变化。这就会触发重排操作。

2,重绘
部分节点需要更新,但是没有改变他的集合形状,比如改变了背景颜色,这就会触发重绘。

下面任何操作都会触发重排reflow:

1、增加或删除可见DOM节点

2、元素改变位置

3、元素改变尺寸(元素内外边距、边框、长度、高度的改变)

4、元素内容改变(元素内容被不同尺寸其它内容代替

5、页面最初呈现

6、浏览器窗口大小改变


设置 display: none;(重排并重绘) 或者 visibility: hidden(只有重绘)
看一个例子:
var bstyle = document.body.style; // cache
 
bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; // another reflow and a repaint
 
bstyle.color = "blue"; // repaint only, no dimensions changed
bstyle.backgroundColor = "#fad"; // repaint
 
bstyle.fontSize = "2em"; // reflow, repaint
 
// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));

有些重绘操作会比其他操作昂贵很多。比如你把一个body的子元素做了修改,不一定会导致大量的其他节点更新,但是你把一个元素移动到页面顶部去,可能就会导致全部其他节点进行重排操作,这个代价就非常昂贵。

聪明的浏览器


因为渲染树的改变导致的重绘或重排操作都可能代价很高,浏览器会对这个改动做很多优化。
一个策略就是不要立即做操作,而是批量进行。比如把你的脚本对DOM的修改放入一个队列,在队列所有操作结束后只需要进行一次绘制即可。

但是有的时候脚本可能会导致浏览器的批量优化无法进行,可能在清空队列之前就需要重新绘制(绘制意思是重绘或者重排)页面。比如你通过脚本获取这些样式:

clientTop/Left/Width/Height
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop/Left/Width/Height
getComputedStyle() 主流浏览器, or currentStyle in IE

备注:clientHeight,height,offsetHeight,scrollHeight的区别可观看height、clientHeight、scrollHeight、offsetHeight区别

因为浏览器必须给你最新的值,所以当你进行这些取值操作的时候会立刻触发一次页面的绘制。这样本来可以批量修改样式然后一次性绘制的方法就无法使用了。

备注:Chrome和Edge可以调用WebGL.API来使用电脑的GPU进行页面渲染,从而给用户更好的体验。


减少重绘和重排


1,不要对元素属性进行读写交替,最好将写操作写完再读。
// bad
var arr=document.getElementsByClassName("demo");
arr[0].style.backgroundColor="red";
console.log(arr[0].style.backgroundColor);
arr[1].style.backgroundColor="green";
console.log(arr[1].style.backgroundColor);
arr[2].style.backgroundColor="blue";
console.log(arr[2].style.backgroundColor);

// better 
var arr=document.getElementsByClassName("demo");
arr[0].style.backgroundColor="red";
arr[1].style.backgroundColor="green";
arr[2].style.backgroundColor="blue";
console.log(arr[0].style.backgroundColor);
console.log(arr[1].style.backgroundColor);
console.log(arr[2].style.backgroundColor);

2、对元素样式的修改,最好一次完成,而不是分为多次进行。

可以使用js的 cssText 进行样式的修改

selector.style.cssText="color:red;background-color:pink;padding:10px";
改变元素的 class 属性,通过不同的class完成修改该元素样式的目的,addClass() removeClass() toggleClass()。

3、把对节点的大量修改操作放在页面之外

用 createDocumentFragment来做修改,而不是createElement
clone 节点,在clone之后的节点中做修改,然后直接替换掉以前的节点
通过 display: none 来隐藏节点(直接导致一次重排和重绘),做大量的修改,然后显示节点(又一次重排和重绘),总共只会有两次重排。
4、不要频繁获取计算后的样式。如果你需要使用计算后的样式,最好暂存起来而不是直接从DOM上读取。

5、总的来说,总是考虑到渲染树得存在,考虑到你的一次修改会导致多大的绘制操作。

比如绝对定位元素的动画就不会影响其他大部分元素。

总结:

1、尽量少访问DOM,一切都在js里解决

2、使用本地对象保存DOM引用

3、HTML collections要时刻跟DOM保持一致,所以访问的时候性能很差。使用本地变量缓存它们的长度,尽量使用本地对象复制操作。

4、重排和重绘都很耗费开销,所以批量修改CSS,修改DOM的时候让节点先离线(使用display或者visibility),本地缓存显示信息。

5、使用CSS选择器函数(querySelector()或者querySelectorAll())代替DOM选择器,因为querySelector()不是动态的。(注:querySelector()返回匹配的第一个元素;querySelectorAll()返回匹配的所有元素)

6、动画动作的时候使用绝对定位(position)

<think>嗯,用户让我详细解释浏览器渲染机制,特别是重排过程。首先,我需要确保自己对这一块的理解是正确的。记得浏览器渲染页面有几个主要步骤,比如解析HTMLCSS,构建DOM树CSSOM树,然后合并成渲染树,再布局制。不过具体每个步骤的细节可能需要再回忆一下。 重排这两个概念经常被提到,但有时候容易混淆。重排应该涉及到布局的改变,也就是几何属性的变化,比如宽度、高度或者位置变化,这样浏览器需要新计算元素的位置大小,这个过程比较耗费性能。而则是当元素的外观发生变化,但不影响布局时发生的,比如颜色、背景色的改变,这时候不需要新布局,直接制即可。不过具体哪些属性会触发重排,哪些触发,可能需要进一步确认。 另外,用户可能想知道如何优化,减少重排的次数。比如合并多次DOM操作,使用文档碎片(DocumentFragment),或者使用CSS的transform属性来做动画,因为这些不会触发重排。可能还需要提到现代浏览器渲染队列机制,它们会批量处理重排,但某些操作会强制刷新队列,比如获取布局信息(offsetTop等),这时候需要注意避免在修改样式后立即读取这些属性。 还有,CSS的某些属性如transformopacity可以利用GPU加速,这样可以在合成层进行处理,避免重排,提升性能。这也是优化的一部分。另外,隐藏元素(display:none)进行操作后再显示,可以避免中间状态的重排,也是一个技巧。 不过,我需要确保这些信息是准确的,可能需要回顾一下浏览器渲染流程的具体步骤,以及各个阶段触发的条件。比如,在渲染树构建完成后,布局阶段计算每个节点的位置大小,制阶段填充像素。当元素变化影响到布局时,会触发重排,进而导致后续元素也需要新布局,这称为“回流”。而如果只是不影响布局的变化,比如颜色,就只需要。 用户可能在实际开发中遇到性能问题,比如频繁操作DOM导致页面卡顿,这时候了解重排的机制就能帮助优化代码。比如使用class一次性修改多个样式,而不是逐个修改,或者将元素脱离文档流(比如使用绝对定位),这样它的变化不会影响其他元素,减少重排的范围。 总结起来,我需要分步骤解释渲染流程,明确重排的定义,触发条件,以及优化方法。可能还需要举一些例子,比如修改width会触发重排,修改color触发,使用transform则不会触发重排。同时,强调两者对性能的影响,尤其是重排的开销更大,应该尽量减少。</think>浏览器渲染机制是通过多阶段流水线将HTML/CSS代码转换为可视化页面,其中(Repaint)重排(Reflow)是影响性能最关键的环节。以下是完整技术解析: $$ \begin{aligned} &\text{关键渲染路径} \\ &1. \text{解析HTML构建DOM树} \rightarrow \text{Parse HTML} \\ &2. \text{解析CSS构建CSSOM树} \rightarrow \text{Parse CSS} \\ &3. \text{合并成渲染树(Render Tree)} \rightarrow \text{Attachment} \\ &4. \text{布局计算(Layout)} \rightarrow \text{Reflow阶段} \\ &5. \text{制(Painting)} \rightarrow \text{Repaint阶段} \\ &6. \text{合成显示(Composite)} \rightarrow \text{GPU加速} \end{aligned} $$ **重排(Reflow)触发条件**: - 几何属性变更:$width$, $height$, $padding$, $margin$等 - 布局相关操作:添加/删除DOM节点、修改$display:none$ - 获取布局信息:$offsetTop$, $scrollHeight$等强制同步布局 **(Repaint)触发条件**: - 非几何样式变更:$color$, $background$, $visibility$等 - 不改变元素在文档流中的位置 **性能对比**: $$ \text{重排成本} \gg \text{成本} \gg \text{合成层操作} $$ **优化策略**: 1. 使用`documentFragment`批量DOM操作 2. 避免逐行读取布局属性(强制同步布局) 3. CSS优化: ```css .box { transform: translateZ(0); /* 启用GPU加速层 */ will-change: transform; /* 预声明变化类型 */ } ``` 4. 使用`requestAnimationFrame`集中样式变更 5. 复杂动画使用`position: absolute`脱离文档流 **现代浏览器优化机制**: - 渲染队列:浏览器会合并连续的重排请求 - 增量重排:仅新计算受影响区域(Dirty Rect) - 合成层:通过`transform``opacity`实现独立光栅化 **调试工具**: - Chrome DevTools > Performance面板可捕获详细渲染过程 - Rendering面板开启`Paint flashing`高亮区域 通过理解这些机制,开发者可以编写出性能提升$3\text{x}$以上的Web应用,特别是在处理动态内容更新时效果显著。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值