0%

浏览器工作原理学习笔记(一)-浏览器概览

本系列为极客时间上的《浏览器工作原理与实践》专栏的学习笔记

浏览器架构变迁

起初浏览器为单进程架构,页面、插件、网络等其他模块都在一个进程下面工作(多个线程),只要有一个线程出现问题,就会导致整个浏览器崩溃。这种架构不稳定、不安全、不流畅。如下图所示:

Markdown

后面架构升级为多进程架构,分为渲染进程、插件进程、网络进程、GPU 进程和浏览器主进程。每个页面和插件都有自己单独的进程,保证了独立性,互不影响,单个页面或者插件出问题之后,不会影响到整个浏览器,将其关闭就好。而且渲染进程被隔离在沙箱内,保证了系统的安全性。如下图所示:

Markdown

未来浏览器的架构方向是面向服务的架构,也就是将浏览器的底层能力划分为一个个基础服务,每个服务在独立的进程内运行,互相通过 IPC 通信。构建一个更内聚、低耦合、易于扩展和维护的系统。如下图所示:

Markdown

浏览器数据包传输

互联网的数据是通过数据包来传输的。如果发送的的数据过大,会拆分成一个个小的数据包进行传输,浏览器的网络传输是基于 TCP/IP 协议簇进行。

IP 协议位于网络层,主要的职责是将数据从一个主机地址传输到另一个主机地址,这里的主机地址就是指的 IP。传输方式是在基础数据包的基础上加上 IP 头信息,里面包含了源 IP 地址、目标 IP 地址等信息。

UDP (用户数据包协议),负责将应用程序通过端口号与网络数据包连接起来。IP 接收到数据包之后,UDP 通过端口号将数据包分发给对应的应用程序。UDP 的特点是:传输速度快,但是不保证传输可靠性(会丢包)。

TCP 协议是一个可靠、稳定、面向连接的传输层协议,它解决了 UDP 的缺点,特点如下:

  • 提供重传机制,解决丢包的问题

  • 大的数据包会拆分成若干个小数据包,接收端收到之后再按照序号拼接

一个完整的 TCP 生命周期包括:建立连接(三次握手)、数据传输、断开连接(四次挥手)

一个完整的 HTTP 请求过程

  1. 构建请求
    • 构建请求行信息
  2. 查找缓存
    • 页面数据本地缓存
    • 缓存对应头字段:Cache-Control,不同的值带来不同的缓存生命周期
  3. 准备 IP 地址和端口
    • IP 地址通过 DNS 查询获取,并且会优先查找本地的 DNS 缓存
  4. 等待 TCP 队列
    • Chrome 对于每个域名只能分配 6 个 TCP 连接
  5. 建立 TCP 连接
  6. 发送 HTTP 请求
    • 请求行、请求头、请求体
    • 如有对应的 Cookie ,会携带,用于持久化登录态
  7. 等到服务端 HTTP 响应
    • 响应行、响应头、响应体
  8. 断开连接/重定向
    • 如果 HTTP 状态码为 301 或 302 ,浏览器需要重定向到另一个地址访问

从输入 URL 到页面展示完整

导航阶段

流程示意图:

Markdown

  1. 用户输入请求信息

  2. 网络进程发起 URL 请求

  3. 服务器响应之后,浏览器进程开始准备渲染进程

  • 如果 HTTP 响应码是 301 或者是 302 ,浏览器会有一个重定向的过程
  • 响应头中的 Content-Type 决定浏览器如何处理响应内容,影响着后续流程,如果是 HTML 页面,那么 Content-Type 的值应该是 text/html
  • 每打开一个页面,浏览器都会准备一个新的进程,有一种情况例外,就是新打开的页面和之前的页面属于同一站点时,这时候会复用前一个进程
  1. 渲染进程准备好之后,就会通知浏览器进程已经做好展示页面状态的准备,这个过程称为“提交文档”

  2. 浏览器进程接收到提交文档的消息后,便会移除旧的文档,同时通知渲染进程文档已提交,渲染进程进入解析页面阶段

    这个过程就叫做 “导航”

渲染流程

Markdown

按照渲染的时间顺序,渲染流水线可以分为如下几个子阶段:

构建 DOM 树 => 样式计算 => 布局阶段 => 分层 => 绘制 => 分块 => 光栅化 => 合成

每个子阶段都可以看成是一个函数,接收输入参数,进行本阶段的处理,然后输出相应的处理结果给下一个子阶段。多个子阶段构成一条渲染流水线。

1. 构建 DOM 树

将简单的 HTML 代码,经由解析器解析,最终转换为一个树状结构,包含节点和内容,方便接下来流程的处理。

2. 样式计算

  • 为了将 CSS 文件转换成浏览器能够理解的结构,先要进行一步转换的操作,解析 CSS 文件,输出 styleSheets

  • 接下来是将CSS 的属性值标准化

  • 然后根据 CSS 继承和层叠的规则,计算每个 DOM 节点的样式

3. 布局阶段

计算 DOM 树中可见节点的几何位置的过程,就叫做布局。

  • 创建布局树,构建一棵只包含可见元素的布局树
  • 布局计算,计算 DOM 树节点的坐标位置

4. 分层

为了完成页面很多复杂的效果,渲染引擎会为一些特定的节点生成对应的图层,最终形成一棵图层树。满足以下两点中的任意一点都会形成一个对应的图层:

  • 拥有层叠上下文属性(定位属性、透明度定义、CSS 滤镜)
  • 需要裁剪的地方(内容溢出容器,就会发生裁剪)

5. 图层绘制

图层的绘制会被分解成一个个绘制指令,形成绘制列表

6. 栅格化(raster)操作

当图层的绘制列表准备好之后,渲染线程的主线程会将绘制列表提交(commit)给合成线程。

合成线程将图层划分为图块,视口附近的图块会被优先转化为位图,生成位图的操作是由栅格化来进行的。栅格化,就是将图块转换为位图。

渲染引擎维护了一个栅格化的线程池,所有的图块栅格化都是在线程池中进行的。

一般栅格化线程都会由 GPU 来加速进行,生成的位图会保存在 GPU 的内存中。

Markdown

7. 合成和显示

所有的图块光栅化之后,合成线程会发出一个绘制图块的命令-“DrawQuad”,提交给浏览器进程。

浏览器进程里有一个叫 viz 的组件,用来接收 DrawQuad 命令,根据这个命令,将页面内容绘制到内存中,最后再显示在屏幕上。

至此,用户就可以看见最终的页面了。