目录
前言:
本文介绍浏览器渲染是如何将网页内容呈现给用户的过程,并概述浏览器渲染的重要性以及它对用户体验的影响。
1. 浏览器是如何渲染页面的
当浏览器的网络线程获取到 HTML 文档开始,浏览器会生成一个渲染任务,并将这个渲染任务放到消息队列下,当事件循环机制轮到此任务时,再提取到渲染主线程,开始渲染流程。这个过程涉及多个阶段,分别是: HTML 解析、样式计算、布局、分层、绘制、分块、光栅化、绘画。每个阶段都有明确的输入输出,上一个阶段的输出会成为下一个阶段的输入。整个渲染流程形成了一套严密的生产流水线,以确保页面能够快速、准确地呈现给用户。
上面是介绍浏览器渲染页面的基本流程,以下是对每个阶段的进一步解释:
1.解析 HTML(Parse HTML)
浏览器在解析 HTML 之前可能会启动一个预解析线程,提前下载 HTML 中引用的外部资源,如 CSS 和 JavaScript 文件。
如果主线程解析到link位置 ,外部 CSS 文件会被异步下载和解析,而不会阻塞 HTML 解析过程。这是因为 CSS 不会改变文档结构,可以并行处理,从而加速页面加载。
相反,外部 JavaScript 文件的下载和执行可能会修改 DOM 结构,因此浏览器会暂停 HTML 解析,等待 JavaScript 文件的下载和执行完成后再继续解析 HTML。这样可以确保在执行 JavaScript 代码之前,DOM 已经完全解析,避免了可能的页面渲染问题。
浏览器解析完成后会得到 DOM 树和 CSSOM 树,DOM 树表示文档的结构和内容,外部 CSS 文件构建 CSSOM 树,表示文档的样式信息。
2.样式计算(Style Calculation)
在样式计算过程中,主线程会遍历得到的 DOM 树,并匹配文档中的每个元素与对应的 CSS 规则,经过选择器匹配、继承处理和优先级计算后,浏览器会得到每个元素的最终样式属性,称之为 Computed Style。
在这一过程中,很多预设值会变成绝对值,比如red
会变成rgb(255,0,0)
;相对单位会变成绝对单位,比如em
会变成px
。
通过样式计算,浏览器可以确定文档中每个元素的最终样式,并获得一棵带有样式的DOM树。
3.布局(Layout)
布局阶段,浏览器会依次遍历 DOM 树的每一个节点 ,并根据每个元素的位置和大小计算出它们在页面中的准确位置,这个过程也被称为重排(Reflow)。
以下是布局阶段的详细解释:
-
盒模型计算:
- 对于每个元素,浏览器首先计算其盒模型(Box Model),包括内容区域、内边距、边框和外边距。
- 盒模型的计算会考虑元素的样式属性,如宽度、高度、内边距、边框大小等。
-
定位计算:
- 对于非静态定位(position 属性不为 static)的元素,浏览器会计算其在页面中的准确位置。
- 对于相对定位(position: relative)、绝对定位(position: absolute)和固定定位(position: fixed)的元素,浏览器会根据相对于其父元素或视口的位置进行定位计算。
-
文档流布局:
- 浏览器会按照文档流的顺序,依次对每个元素进行布局计算。
- 文档流布局是基于元素在 HTML 文档中出现的顺序,元素的位置会受到其前面元素的影响。
-
浮动元素处理:
- 如果页面中存在浮动元素(float 属性不为 none),浏览器会对其进行处理,确保其他元素正确布局。
- 浮动元素的布局会影响周围元素的位置和大小,因此浏览器需要重新计算这些受影响的元素的布局。
-
清除浮动:
- 如果某个元素的子元素都是浮动元素,而它自身不是浮动的,那么它的高度可能会塌陷。在布局阶段,浏览器会计算并应用清除浮动的方法,以避免高度塌陷问题。
-
自适应布局:
- 浏览器会考虑页面的自适应布局,确保页面在不同设备和窗口大小下能够正确显示。
- 这包括响应式设计、弹性布局等技术,以确保页面在各种分辨率和屏幕尺寸下都能提供良好的用户体验。
在布局阶段,有一些特殊情况会影响到 DOM 树和布局树之间的对应关系,导致它们无法完全一一对应。以下是一些常见情况:
-
display:none:
- 元素如果设置为 display: none,表示该元素不会在页面中显示,并且不会占据任何空间。因此,这些元素不会生成到布局树中,布局过程中也不会考虑它们的位置和大小。
-
伪元素:
- 使用伪元素选择器(如 ::before 和 ::after)可以在元素的内容前后插入虚拟的元素,这些虚拟元素在布局过程中会生成对应的几何信息,因此会被包含在布局树中。
-
匿名行盒和匿名块盒:
- 当文档中的内容不足以生成行盒或块盒时,浏览器会自动生成匿名的行盒或块盒来容纳这些内容。
- 匿名行盒通常出现在文本行内,用于容纳一行文本内容;匿名块盒通常出现在块级元素中,用于容纳块级元素的子元素。
- 这些匿名盒子在布局过程中会被添加到布局树中,但它们在 DOM 树中并没有对应的实际节点。
布局阶段是浏览器渲染过程中的关键步骤之一,它确定了页面中每个元素的准确位置和大小,为后续的绘制过程提供了基础。优化布局过程可以显著提高页面的加载速度和性能,因此对布局原理的深入理解是网页开发中的重要知识点。
4.分层(Layering)
分层指的是将页面中的元素按照一定规则分成多个图层,每个图层可以独立地进行绘制和渲染,以提高页面渲染性能和用户体验。
分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。
一般情况下,以下情况会触发分层:
- 元素拥有3D或透视变换(perspective)属性。
- 元素拥有 CSS 动画或过渡效果。
- 元素是
<video>
、<canvas>
或<iframe>
等特殊元素。 - 元素拥有 CSS 滤镜效果(如 blur、drop-shadow 等)。
- 元素拥有 CSS 混合模式(blend mode)效果。
也可以通过will-change
属性更大程度的影响分层结果。
5.绘制(Painting)
在绘制阶段下,渲染主线程会负责生成每个图层的绘制指令集,描述了如何将每个图层的内容绘制出来。这些绘制指令集包括了元素的位置、大小、颜色、阴影等信息,以及如何对页面元素进行变换、裁剪等操作。最后将渲染树中的每个元素转换为屏幕上的实际像素,形成绘制表面(Paint Surface),这个过程也称为重绘(Repaint)。
完成绘制后,主线程将每个图层的绘制信息提交给合成线程,剩余工作将由合成线程完成。
6.分块(Tiling)
分块是指将页面的图层划分为多个小块区域的过程。这个过程是由合成线程负责的,它会从线程池中拿取多个线程来完成分块工作。
分块的方法通常是根据图层的大小和复杂度来确定的。较大或较复杂的图层可能会被划分为更多的小块,而较小或简单的图层则可能只需划分为少数几个小块。分块的大小和数量通常会根据设备的性能和屏幕的分辨率进行调整,以实现最佳的渲染性能。
分块过程通常会考虑到每个小块的尺寸、图层的边界以及图层之间的关系,以确保分块后的小块能够正确地覆盖整个图层。
7.光栅化(Rasterization)
光栅化是指将图形对象转换为屏幕上的像素也就是位图,矢量图形(如线条、路径等)也会被转换为像素图形。并且该过程支持硬件加速,即利用 GPU 等硬件资源加速光栅化过程,提高渲染效率和性能。
在进行光栅化时,通常会采取一些优化策略,其中一个重要的优化策略是优先处理靠近视口的块。这是因为靠近视口的块对用户来说更为重要,它们在屏幕上的可见性更高,因此优先处理这些块可以提高页面的显示速度和用户体验。
8.合并(Compositing)
合并是渲染的最后一个阶段,是指将多个图层的内容合并成最终的页面图像的过程。在合并阶段,浏览器会将经过光栅化处理的各个图层按照一定的顺序进行组合,并交给 GPU 进⾏,形成最终的渲染结果。
2.重排(Reflow)
浏览器渲染过程中,当页面布局发生改变时,浏览器需要重新计算元素的几何属性(如位置、大小)并重新排列,这个过程叫做重排。重排可能由多种原因引起,比如改变元素的尺寸、位置、内容,以及修改 CSS 样式等。
为了避免连续的多次操作导致布局树反复计算,浏览器会将这些操作合并起来,等待 JavaScript 代码全部完成后再进行统一的布局计算。因此,改动属性引发的重排操作是异步完成的,浏览器会在适当的时机进行批量处理,提高渲染性能。
然而,正如你所提到的,这种异步特性可能会导致在 JavaScript 中获取布局属性时无法获取到最新的布局信息,因为获取属性的操作可能在重排操作之前执行。为了解决这个问题,浏览器在某些情况下会选择立即进行重排,以确保 JavaScript 能够获取到最新的布局信息。这种权衡考虑是为了平衡渲染性能和 JavaScript 代码的准确性。
如何优化:
- 避免频繁的 DOM 操作:减少对 DOM 结构的修改次数,可以减少重排的次数。
- 批量处理样式修改:尽量使用 CSS 类来批量修改样式,而不是直接操作元素的样式属性。
- 使用文档碎片(Document Fragment):在需要添加大量 DOM 元素时,先创建文档碎片来批量添加元素,然后一次性将文档碎片添加到页面中。
- 避免强制同步布局:避免在 JavaScript 中频繁获取元素的布局信息,可以使用异步布局方法(如 requestAnimationFrame)来优化布局计算。
3.重绘(Repaint)
与重排不同,重绘只是重新绘制元素的外观,而不会影响其位置或大小。
重绘的本质确实是根据分层信息重新计算绘制指令,然后将页面元素的外观重新绘制到屏幕上。当改变了元素的可见样式时,会触发重绘操作,因为浏览器需要根据新的样式信息重新计算绘制指令,并将修改后的外观呈现在屏幕上。
重绘是针对页面元素的外观进行的操作,与布局信息无关。但需要注意的是,由于元素的布局信息也属于可见样式的一部分,所以重排(reflow)一定会引起重绘。这是因为在重新计算布局信息时,浏览器不仅需要更新元素的位置和大小,还需要重新计算绘制指令,以确保页面的外观与布局信息相匹配。
因此,当页面发生布局变化时,重绘操作是不可避免的。为了提高页面的渲染性能,可以尽量减少不必要的布局变化和样式修改,从而减少重绘操作的触发次数。
优化重绘:
- 合并样式修改:尽量批量修改样式,避免多次单独修改样式属性,以减少不必要的重绘操作。
- 使用 CSS3 动画或过渡效果:CSS3 动画和过渡效果是通过 GPU 加速实现的,可以减少重绘操作,提高性能。
- 硬件加速:现代浏览器通常支持硬件加速,可以利用 GPU 加速重绘操作,提高渲染效率。
结尾:
在浏览器渲染页面的复杂过程中,我们探索了诸多重要概念,包括HTML解析、样式计算、布局、分层、绘制、分块、光栅化、重排和重绘等。这些概念相互交织,共同构成了浏览器如何将网页呈现给用户的全貌。
深入理解这些概念,不仅让我们对网页渲染的机制有了更清晰的认识,还启示了我们如何优化网页性能,提升用户体验。通过合理设计网页结构、优化样式和布局、减少重排和重绘等手段,我们能够更高效地渲染页面,使用户能够更快速、流畅地浏览网页内容。
在未来的网页开发中,我们可以更加巧妙地利用这些知识,创造出更具吸引力、性能更佳的网页,为用户带来更加优质的浏览体验。
参考资料:
- W3C(World Wide Web Consortium)官方文档:W3C 对于 HTML、CSS、JavaScript 等标准的官方文档提供了详尽的说明。
- MDN Web 文档:MDN 提供了关于 Web 技术的详细文档和教程,涵盖了 HTML、CSS、JavaScript 等方面的内容。
- Google Developers 文档:Google Developers 提供了关于前端开发和性能优化的丰富资料,包括浏览器渲染原理、性能优化技巧等。
- 渡一教育《WEB前端大师课》部分课件。