宏观视角下的浏览器

257 阅读10分钟

浏览器工作原理

线程 vs 进程

多线程可以并行处理任务,线程是不能单独存在的,它是由进程来启动和管理的。

一个进程就是一个程序的运行实例。

线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。

线程和进程的关系

  1. 进程中的任意一线程执行出错,都会导致整个进程的崩溃。
  2. 线程之间共享进程的数据。
  3. 当一个进程关闭之后,操作系统会回收进程所占内存。
  4. 进程之间的内容相互隔离。
  • 浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
  • 渲染进程。核心任务是将HTML、CSS和JavaScript转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript引擎V8都是运行在该进程中,默认情况下,Chrome会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
  • GPU 进程。其实,Chrome刚开始发布的时候是没有GPU进程的。而GPU的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome在其多进程架构上也引入了 GPU 进程。
  • 网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
  • 插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

Tcp协议

1. IP:把数据包送达目的主机。网际协议(Internet Protocol,简称 IP)

2. UDP:把数据包送达应用程序,UDP 不能保证数据可靠性,但是传输速度却非常快。 用户数据包协议(User Datagram Protocol),简称 UDP

3. TCP:把数据完整地送达应用程序。TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

对于数据包丢失的情况,TCP提供重传机制;
TCP引入了数据包排序机制,用来保证把乱序的数据包组合成一个完整的文件。

tcp连接生命周期

1.建立连接。这个阶段是通过“三次握手”来建立客户端和服务器之间的连接。三次握手,是指在建立一个TCP连接时,客户端和服务器总共要发送三个数据包以确认连接的建立。
2.传输数据。当前阶段接收端需要对每个数据包进行确认操作
3.断开连接。“四次挥手”来保证双方都能断开连接

TCP 为了保证数据传输的可靠性,牺牲了数据包的传输速度,因为“三次握手”和“数据包校验机制”等把传输过程中的数据包的数量提高了一倍。

HTTP请求流程

http是建立在tcp基础上的。HTTP是一种允许浏览器向服务器获取资源的协议,是Web的基础

输入一个地址发生了如下事情:

  • 1.浏览器构建请求行信息(如下所示),构建好后,浏览器准备发起网络请求。
        GET /index.html HTTP1.1
    
    1. 差找缓存。浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术。
    1. 准备IP地址和端口号。HTTP 协议作为应用层协议,TCP/IP 作传输层协议,HTTP 的内容是通过 TCP 的传输数据阶段来实现的。
      域名映射为 IP 的系统就叫做“域名系统”,简称 DNS(Domain Name System)。 第一步浏览器会请求 DNS 返回域名对应的 IP。当然浏览器还提供了 DNS 数据缓存服务,如果某个域名已经解析过了,那么浏览器会缓存解析的结果,以供下次查询时直接使用,这样也会减少一次网络请求。
  • 4.等待Tcp队列
  • 5.建立Tcp连接
  • 6.发送http请求

服务器端处理 HTTP 请求流程

    1. 返回请求
    1. 断开连接
    1. 重定向

浏览器缓存和DNS缓存

浏览器缓存

当服务器返回 HTTP 响应头给浏览器时,浏览器是通过响应头中的 Cache-Control字段来设置是否缓存该资源,通常,我们还需要为这个资源设置一个缓存过期时长,而这个时长是通过 Cache-Control 中的 Max-age 参数来设置的,比如上图设置的缓存过期时间是 2000 秒。

    Cache-Control:Max-age=2000

意味着资源未过期的情况下,再次请求会直接返回缓存的资源给浏览器。

如果缓存过期了会继续发起网络请求,并在HTTP 请求头带上

    If-None-Match:"4f80f-13c-3a1xb12a"

服务器收到请求头后,根据If-None-Match判断请求的资源有无更新

  • 若无更新,返回304状态码,等于服务器告诉浏览器缓存可以继续使用,不重复发送数据。
  • 若有更新,服务器返回最新资源给浏览器

http请求示意图

从输入URL到页面展示,这中间发生了什么

从输入 URL 到页面展示完整流程示意图

  • 首先,浏览器进程接收到用户输入的url请求,浏览器进程将url转发给网络进程;
  • 然后在网络进程中发起真正的url请求;
  • 接着网络进程接收到响应头的数据,解析响应头数据,数据再转发给浏览器进程;
  • 浏览器进程接收到网络进程的响应头数据后,发送“提交导航(CommitNavigation)”消息到渲染进程;
  • 渲染进程接收到提交导航消息之后,开始准备节后html数据,接收数据的方式是直接和网络进程建立数据管道
  • 最后渲染进程会向浏览器进程“确认提交”,这是告诉浏览器进程:“已经准备好接受和解析页面数据了”。
  • 浏览器进程接收到渲染进程"提交文档"的消息之后,便开始移除之前旧的文档,然后更新浏览器进程中的页面状态。

用户发出 URL 请求到页面开始解析的这个过程,就叫做导航。

从输入 URL 到页面展示

1. 用户输入

判断是搜索内容还是请求URL

搜索内容: 使用默认搜索引擎+关键字生成新的url
内容符合url规则:根据规则加上协议合成完整的URL

2. URL请求过程

首先,网络进程会查找本地缓存事发后缓存了本地资源,如有直接返回给浏览器,若无进入网络请求流程,请求前的第一步是进行DNS解析,以获取请求域名的服务器IP地址,如果是HTTPS请求还要TLS连接。

接下来利用IP地址和服务器建立TCP连接。浏览器端构建请求行请求头等信息,并把和域名相关的Cookie等数据附加到请求头中,然后像服务器发送构建请求信息。 服务器接收到请求信息后,根据请求信息生成相应数据(响应行,响应头,响应体等),发给网络进程。网络进程接收到响应行和响应头之后,解析响应头内容。

(1)重定向

在接收到服务器的响应头之后,网络进程解析响应头,状态码301或302需要浏览器重定向到其他URL,这时网络进程会从响应头的location字段读取重定向的地址,重新发起请求。 (2)响应数据类型 浏览器根据Content-Type区分URL请求的数据类型,判断是下载类型还是正常的 HTML 页面。

Content-Type告诉浏览器服务器返回的响应体数据是什么类型

3. 准备渲染过程

如果从一个页面打开了另一个新页面,而新页面和当前页面属于同一站点的话,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫 process-per-site-instance。

渲染进程策略:通常情况下,打开新的页面都会使用单独的渲染进程;如果从 A 页面打开 B 页面,且 A 和 B 都属于同一站点的话,那么 B 页面复用 A 页面的渲染进程;如果是其他情况,浏览器进程则会为 B 创建一个新的渲染进程。

4. 提交文档

意思是浏览器进程将网络进程接收到的HTML数据提交给渲染进程

  • 首先当浏览器接收到网络进程的响应头数据之后,向渲染进程发送提交文档的消息
  • 渲染进程接收到提交文档的消息之后,会和网络进程建立传输数据的管道
  • 等文档数据传输之后,渲染进程会返回确认提交的消息给浏览器进程
  • 浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面。

5. 渲染阶段

构建DOM树

样式计算
  1. 把 CSS 转换为浏览器能够理解的结构。当渲染引擎接收到CSS文本时,会执行一个转换操作,将 CSS文本转换为浏览器可以理解的结构——styleSheets。
  2. 转换样式表中的属性值,使其标准化
  3. 计算出 DOM 树中每个节点的具体样式

布局阶段

计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局

  1. 创建布局树

    遍历 DOM 树中的所有可见节点,并把这些节点加到布局中;

    而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。

  2. 布局计算

    一个完整的渲染流程大致可总结为如下:

    • 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
    • 渲染引擎将 CSS -样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
    • 创建布局树,并计算元素的布局信息。
    • 对布局树进行分层,并生成分层树。
    • 为每个图层生成绘制列表,并将其提交到合成线程。
    • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
    • 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
    • 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

减少重排重绘, 方法很多:

  1. 使用 class 操作样式,而不是频繁操作 style
  2. 避免使用 table 布局
  3. 批量dom 操作,例如 createDocumentFragment,或者使用框架,例如 React
  4. Debounce window resize 事件
  5. 对 dom 属性的读写要分离
  6. will-change: transform 做优化

参考资料

浏览器工作原理与实践