浏览器的渲染流程:从 HTML 到屏幕显示

在我们日常使用浏览器浏览网页时,往往忽略了浏览器背后复杂的渲染过程。从输入 URL 到页面最终显示在屏幕上,浏览器需要经过一系列精心设计的步骤。

浏览器渲染的整体流程

浏览器的渲染流程可以大致分为两个主要部分:网络渲染。当用户在地址栏输入 URL 并按下回车键时,浏览器的网络线程会发送 HTTP 请求与服务器通信,获取 HTML 文档。获取到 HTML 文档后,浏览器会将其封装成一个渲染任务,并将其传递给渲染主线程的消息队列。在事件循环机制的作用下,渲染主线程取出消息队列中的渲染任务,开启渲染流程。

渲染流程主要分为以下几个阶段:

  1. 解析 HTML:生成 DOM 树。
  2. 样式计算:计算每个 DOM 节点的最终样式。
  3. 布局:计算每个节点的几何信息。
  4. 分层:将页面划分为多个图层。
  5. 生成绘制指令:为每个图层生成绘制指令集。
  6. 分块:将每个图层划分为更小的块。
  7. 光栅化:将每个块转换为位图。
  8. 绘制:将位图绘制到屏幕上。

1. 解析 HTML

解析 HTML 是渲染流程的第一步,其目标是将 HTML 字符串解析为 DOM 树。浏览器接收到 HTML 文件后,会将其从字节数据转换为字符串,然后通过词法分析将其转换为标记(token)。标记化后的数据将被用来构建 DOM 树。

在解析 HTML 的过程中,浏览器可能会遇到 <style><link><script> 标签。为了提高解析效率,浏览器会启动一个预解析线程,优先下载外部 CSS 文件和外部 JS 文件。如果主线程解析到 <link> 标签时,外部 CSS 文件尚未下载解析完成,主线程不会等待,而是继续解析后续的 HTML。这是因为 CSS 的下载和解析工作是在预解析线程中进行的,这也是 CSS 不会阻塞 HTML 解析的根本原因。

然而,如果主线程解析到 <script> 标签时,会停止解析 HTML,等待 JS 文件下载完成,并执行全局代码后,才会继续解析 HTML。这是因为 JS 代码的执行可能会修改当前的 DOM 树,所以 DOM 树的生成必须暂停。这就是 JS 会阻塞 HTML 解析的根本原因。

2. 样式计算

拥有了 DOM 树后,浏览器还需要为每个节点计算最终的样式。这一过程称为样式计算。主线程会遍历 DOM 树,为每个节点计算出其最终的样式,生成样式规则树。在这一过程中,预设值会变成绝对值,相对单位会变成绝对单位。

3. 布局

样式计算完成后,浏览器需要计算每个节点的几何信息,这一过程称为布局。主线程会遍历 DOM 树,根据计算样式计算出每个节点的几何信息,生成布局树。布局树上每个节点会有其在页面上的 x、y 坐标以及盒子大小的具体信息。

需要注意的是,布局树与 DOM 树并非一一对应。例如,display: none 的节点不会生成到布局树中,而伪元素选择器虽然在 DOM 树中不存在,但会生成到布局树中。

4. 分层

布局完成后,浏览器会进行分层。分层的目的是为了提高渲染效率。主线程会遍历布局树,根据特定的规则(如滚动条、堆叠上下文、transformopacity 等)将页面划分为多个图层。

5. 生成绘制指令

分层完成后,主线程会为每个图层生成绘制指令集。这些指令集描述了如何绘制每个图层的内容。生成绘制指令后,主线程的工作暂时告一段落,接下来将绘制信息提交给合成线程。

6. 分块

合成线程接收到绘制信息后,会将每个图层划分为更小的块。这一过程称为分块。分块的目的是为了进一步提高渲染效率。

7. 光栅化

分块完成后,合成线程会将块信息交给 GPU 进程进行光栅化。光栅化的目的是将每个块转换为位图,确定每个像素点的 RGB 信息。

8. 绘制

最后一步是绘制。合成线程会将每个图层的位图绘制到屏幕上。这一过程称为绘制。

常见面试题

1. 什么是 reflow?

reflow 的本质是重新计算布局树。当进行了会影响布局树的操作后,需要重新计算布局树,会引发 reflow。为了避免连续的多次操作导致布局树反复计算,浏览器会合并这些操作,当 JS 代码全部完成后再进行统一计算。所以,改动属性造成的 reflow 是异步完成的。然而,当 JS 获取布局属性时,可能会造成无法获取到最新的布局信息。为了避免这一问题,浏览器决定获取属性时立即 reflow。

2. 什么是 repaint?

repaint 的本质是重新根据分层信息计算绘制指令。当改动了可见样式后,需要重新计算,会引发 repaint。由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint。

3. 为什么 transform 的效率高?

transform 的效率高是因为它既不会影响布局也不会影响绘制指令,它影响的只是渲染流程的最后一个「draw」阶段。由于 draw 阶段在合成线程中,所以 transform 的变化几乎不会影响渲染主线程。反之,渲染主线程无论如何忙碌,也不会影响 transform 的变化。

总结

浏览器的渲染流程是一个复杂而精细的过程,涉及多个阶段和多个线程的协同工作。从解析 HTML 到最终绘制页面,每个阶段都有其特定的任务和目标。了解浏览器的渲染流程不仅有助于我们更好地理解网页是如何呈现的,还能帮助我们在开发中优化性能,减少不必要的重排和重绘。

相关推荐
步行cgn13 分钟前
Vue 中的数据代理机制
前端·javascript·vue.js
GH小杨18 分钟前
JS之Dom模型和Bom模型
前端·javascript·html
星月心城1 小时前
JS深入之从原型到原型链
前端·javascript
MessiGo1 小时前
Javascript 编程基础(5)面向对象 | 5.2、原型系统
开发语言·javascript·原型模式
你的人类朋友2 小时前
🤔Token 存储方案有哪些
前端·javascript·后端
烛阴2 小时前
从零开始:使用Node.js和Cheerio进行轻量级网页数据提取
前端·javascript·后端
liuyang___2 小时前
日期的数据格式转换
前端·后端·学习·node.js·node
西哥写代码2 小时前
基于cornerstone3D的dicom影像浏览器 第三十一章 从PACS服务加载图像
javascript·pacs·dicom
潘小磊3 小时前
高频面试之5Kafka
面试·职场和发展
Frankabcdefgh3 小时前
Python基础数据类型与运算符全面解析
开发语言·数据结构·python·面试