有一种知识叫,你不知道的浏览器基础

667 阅读9分钟

这是我参与更文挑战的第 15 天,活动详情查看: 更文挑战

有一种知识叫,你不知道的浏览器基础

1.浏览器导航流程

关于这个点,很喜欢出一个面试题:从输入URL到页面展示,这中间发生了什么?因此本节就围绕这个来讲

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

1.1.1 基本流程简述

  • 首先,用户从浏览器进程里输入请求信息;
  • 然后,网络进程发起 URL 请求;
  • 服务器响应 URL 请求之后,浏览器进程就又要开始准备渲染进程了;
  • 渲染进程准备好之后,需要先向渲染进程提交页面数据,称之为提交文档阶段;
  • 渲染进程接收完文档信息之后,便开始解析页面和加载子资源,完成页面的渲染。

1.1.2 用户输入

当用户在地址栏中输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容,还是请求的 URL。

  • 如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL。
  • 如果判断输入内容符合 URL 规则,比如输入的是 tangjievic.top,那么地址栏会根据规则,把这段内容加上协议,合成为完整的 URL,如 tangjievic.top。 这里看过掘金上的一篇文章,谈的是阿里对这部分知识面试提问

在这个点上,阿里喜欢修福报的面试官提出了一个灵魂问题:url为啥要解析,dns查询规则是什么?

关于url为什么要解析:

  • 因为网络标准规定了URL只能是字母和数字,还有一些其它特殊符号(-_.~ ! * ’ ( ) ; : @ & = + $ , / ? # [ ]
  • 然后url对地址栏参数有一定的格式,当你的参数和值和此格式又冲突时,游览器会进行转义
  • 综上2点,url有自己的一套编码规则,所以当判断用户输入符合url规则就开始进行dns查询

1.1.3 URL请求过程

当识别出是url后,浏览器进程会通过进程间通信(IPC)把 url 请求发送至网络进程,网络进程接收到 URL 请求后,会在这里发起真正的 URL 请求流程

流程:

  1. 首先,网络进程会查找本地缓存是否缓存了该资源。如果有缓存资源,那么直接返回资源给浏览器进程(作系统会先查hosts件是否有记录,有的话就会把相对应映射的IP返回)

  2. 如果在缓存中没有查找到资源,那么直接进入网络请求流程。这请求前的第一步是要进行 DNS 解析,以获取请求域名的服务器 IP 地址。如果请求协议是 HTTPS,那么还需要建立 TLS 连接。 接下来就是利用 IP 地址和服务器建立 TCP 连接。连接建立之后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。

  3. 服务器接收到请求信息后,会根据请求信息生成响应数据(包括响应行、响应头和响应体等信息),并发给网络进程。等网络进程接收了响应行和响应头之后,就开始解析响应头的内容了

好到这里接到上面的灵魂提问:dns查询规则是什么?

  1. 递归查询

就是主机所查询的本地域名服务器不知道被查询的域名的IP地址,那么本地域名服务器就以DNS客户的身份向其根域名服务器发送查询请求报文,如果没有,根域名服务器再向顶级域名服务器查询,如此反复直到出最终结果 图示:

7-1.png

  1. 迭代递归结合查询 目前的主流方式 主机和本地域名服务器采用递归查询,本地服务器和其他服务器采用迭代查询,迭代查询具体是指:当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP地址,要么告诉本地服务器:“你下一步应当向哪一个域名服务器进行查询”。

图示:

7-2.png

dsn的问题解决了,那么回到正轨,谈一谈这一过程出现的其他问题

  • (1)重定向 在接收到服务器返回的响应头后,网络进程开始解析响应头,如果发现返回的状态码是 301 或者 302,那么说明服务器需要浏览器重定向到其他 URL。这时网络进程会从响应头的 Location 字段里面读取重定向的地址,然后再发起新的 HTTP 或者 HTTPS 请求,一切又重头开始了。

服务器返回的响应头的状态码是 200,这是告诉浏览器一切正常,可以继续往下处理该请求了。

  • (2)响应数据类型处理 Content-Type 是 HTTP 头中一个非常重要的字段, 它告诉浏览器服务器返回的响应体数据是什么类型,然后浏览器会根据 Content-Type 的值来决定如何显示响应体的内容。

1.1.4 准备渲染进程

默认情况下,Chrome 会为每个页面分配一个渲染进程,也就是说,每打开一个新页面就会配套创建一个新的渲染进程。但是,也有一些例外,在某些情况下,浏览器会让多个页面直接运行在同一个渲染进程中。

那什么情况下多个页面会同时运行在一个渲染进程中呢?

如果从一个页面打开了另一个新页面,而新页面和当前页面属于同一站点的话,那么新页面会复用父页面的渲染进程

1.1.5 提交文档

首先要明确一点,这里的“文档”是指 URL 请求的响应体数据。

“提交文档”的消息是由浏览器进程发出的,渲染进程接收到“提交文档”的消息后,会和网络进程建立传输数据的“管道”。 等文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程。 浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面。 如图:

7-3.png

这也就解释了为什么在浏览器的地址栏里面输入了一个地址后,之前的页面没有立马消失,而是要加载一会儿才会更新页面。

1.1.6 渲染阶段

一旦文档被提交,渲染进程便开始页面解析和子资源加载了

基本上就是:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成,在下面进行详细说明

2. 渲染流程

2.1 构建 DOM 树

构建 DOM 树的输入内容是一个非常简单的 HTML 文件,然后经由 HTML 解析器解析,最终输出树状结构的 DOM。

2.2 样式计算

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

2.3 布局

  1. 计算出 DOM 树中可见元素的几何位置

  2. 创建布局树,DOM 树中所有不可见的节点都没有包含到布局树中。 布局计算 经过以上的2步基本上布局基本形成

2.4 分层

针对一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。如果ps里面的图层一样的概念。

这里也是web页面优化的一个重要导航点

2.5 绘制

经过了构建dom树,css规则树,布局,分层,这样一个基本的渲染树形成,就开始进行绘制

  • 为每个图层生成绘制列表,并将其提交到合成线程。

  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。

  • 合成线程发送绘制图块命令给浏览器进程。

  • 浏览器进程根据绘制图块命令消息生成页面,并显示到显示器上。 这里有一个web前端优化很重要的点也是渲染流水线相关的概念——“重排”“重绘”和“合成”

    1. 更新了元素的几何属性(重排)

7-4.png

从上图可以看出,如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。

同时重排很可能引起大面积回流 回流是指浏览器为了重新渲染部分或者全部的文档而重新计算文档中元素的位置和几何构造的过程。因为回流可能导致整个dom树的重新构造,所以会影响性能。

避免大面积回流的方式就是对该元素使用定位,建立独立的图层,一次来减少回流带来的影响

  • 2.更新元素的绘制属性(重绘)

7-5.png

从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

3.直接合成阶段 渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成

7-6.png

使用了CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率