从输入 URL 到页面展示

· 1658 words · 9 min

从输入 URL 到页面展示过程

整个过程大致可描述如下:

其中,URL 请求到页面开始解析的这个过程,叫做导航。

用户输入

浏览器根据用户输入的内容合成成完整的 URL。在发起请求之前,浏览器会询问当前页面要不要执行 beforeunload 事件, 比如处理未提交的表单等。用户也可以通过 beforeunload 取消导航。若没有 beforeunload 事件, 浏览器便开始进入加载状态,但是没有立即替换页面内容。

请求和响应

浏览器建立请求完成之后,得到了服务器返回的响应数据。接下来浏览器根据 Content-Type 响应头来决定如何处理响应。

准备渲染进程

默认情况下,浏览器会给每个页面开启一个渲染进程,例外情况下,浏览器会让多个页面运行在同一个渲染进程中。 这个例外情况指的就是当页面属于同一站点时。同一站点的定义为:根域名、协议、子域名和端口。 例如下面的三个网址都属于同一站点,协议都是 HTTPS,根域名也都是 geekbang.org。

https://time.geekbang.org
https://www.geekbang.org
https://www.geekbang.org:8080

Chrome 的默认策略是,每个标签对应一个渲染进程。但是如果一个页面打开了两一个新的页面, 新页面和当前页面有属于同一站点的时候,新页面就会复用父页面的渲染进程。 官方把这个策略叫做 procee-per-site-instance

渲染进程准备好之后,还不能立即进入文档解析阶段,此时文档还在网络进程里。

提交文档

浏览器导航完成状态如下,之后便进入了渲染阶段。

浏览器界面更新

渲染阶段

渲染阶段被分为很多的子阶段,这样的处理流程叫做渲染流水线。按照时间顺序, 流水线可分为如下几个子阶段:构建 DOM 树,样式计算,布局阶段,分层,绘制,分块,光栅化和合成。

构建 DOM 树

DOM 树和 HTML 的内容几乎是一样的,但 DOM 是保存在内存中的树状结构,可以通过 JS 来查询或修改。

样式计算

该过程分为三步:

  1. CSS 转换为浏览器可以理解的结构:styleSheets。
  2. 转换样式中的属性值,使其标准化。例如:
标准化样式属性
  1. 计算出 DOM 树中每个阶段的具体样式。涉及到 CSS 的继承规则和层叠规则。要了解每个 DOM 元素的最终计算样式,可以在开发者工具栏中查看 “Elements/Computed” 一栏。
计算 DOM 树中的具体样式

布局阶段

计算出 DOM 树中可见元素的几何位置的过程称为布局。在显示之前,需要额外的构建一颗只包含可见元素的布局树, 不可见的节点会被忽略掉,比如 head 标签下的全部内容,比如 display: none 的元素。

分层

生成布局树之后,渲染引擎还需要为特定的节点生成专用的图层,并生成一颗对应的图层树。 浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。Chrome 开发者工具“Layers”查看分层情况。 如果节点没有自己的图层,那么就属于父节点的图层。图层的分层规则:

分层

图层绘制

构建完图层之后,渲染引擎会对图层进行绘制。渲染引擎将图层的绘制拆解为很多小的绘制指令(分块)。 然后把这些指令按顺序组成一个待绘制的列表。打开 Chrome 的 “Layout/document” 可以看到。

对图层进行绘制

栅格化操作

拆解成绘制命令之后,实际的绘制操作由渲染引擎中的合成线程来完成。主线程把绘制列表“提交”(commit)给合成线程。

某些情况下,比如图层很大(页面很长的时候),用户只能通过视口看到一页面的一小部分, 这种情况下,绘制出所有的图层内容会产生很大的开销,完全没有必要。基于这个原因,合成线程会将图层划分为图块(tile), 图块的大小通常为 256x256 或 512x512。

图层划分为图块

合成线程会按照视口附近的图块优先生成位图。图块 → 位图的过程称为栅格化。图块是栅格化的最小单位, 渲染进程维护了一个栅格化的线程池,所有的栅格化操作都是在线程池内进行的。通常栅格化过程都会使用 GPU 来加速, 使用 GPU 生成位图的过程叫做快速栅格化,或称为 GPU 栅格化,生成的位图保留在 GPU 内存中。

渲染进程

合成和显示

所有的图块被栅格化之后,合成线程会生成绘制图块的命令“DrawQuad”,然后将其发送给浏览器进程。 浏览器进程将页面绘制到内存中,然后将内存显示在屏幕上。

经过一系列阶段后,浏览器就会显示出页面了。

From 极客时间