浏览器从输入URL到渲染页面过程发生了什么?

刚接触前端的时候,看过很多类似的文章,但因为文章里面提及的知识点自己没有接触过,没理解,所以当下看了记下了,过后也就忘记了,经过工作后再总结一次。

这里会提到以下几个过程:

  1. 查询缓存

  2. DNS域名解析

  3. 建立 TCP 连接

  4. 向服务器发送 HTTP 请求,返回结果

  5. 浏览器解析 HTML

  6. 浏览器布局渲染

以 baidu.com 为例,当我们在浏览器上输入URL 的时候,其实就是我们要向服务器请求我们想要的内容。

查询缓存

这里,要提到一点,缓存问题,当客户端发起一个 HTTP 请求时,不会说立即发到服务器处,而是从浏览器缓存里面找匹配。

而浏览器缓存分为协商缓存与强制缓存,当浏览器发起 HTTP 请求时,会先根据 HTTP 的请求头里面,判断是否有强缓存,如果存在强缓存且没过期则命中返回,这个时候就不会再像服务器发起请求。如果强缓存没有命中,就继续像服务器发起请求,但在正式向服务器发起请求前,会有一系列的准备动作,这里就会提到 DNS 域名解析了。

DNS域名解析

当输入 url 在向服务器发起请求前,浏览器要首先确定,这个域名所对应的服务器在哪里。而将域名转换成服务器地址就是由DNS解析器来完成。

客户端收到域名的时候,首先会从我们的本地HOSTS文件去找,检查文件是否存在域名与IP的对应关系(所以我们一般开发或者测试,可以通过配置HOSTS文件从而达到访问开发环境、测试环境、回归环境的目的),如果本地HOSTS文件存在该关系,那就按这个IP去访问服务器;

如果没有,则再去找本地DNS解析器缓存,如果存在该关系,则返回结果,完成DNS解析;

如果还是没有,首先会找TCP/ip参数中设置的首选DNS服务器,也就是本地DNS服务器,如果存在该映射关系则返回;

如果不存在,则请求根DNS服务器->com DNS服务器(根据域名判断)->baidu.com DNS服务器(如本地DNS服务器有设置转发器则不请求根DNS服务器,直接转发到上一级的DNS服务器进行解析返回)。

(以下图片引用DNS原理及其解析过程)

建立 TCP 连接

通常说到拿到 DNS 解析后,在与服务器发起请求前,我们需要先与服务器建立连接,这里就要提到 TCP 的三次握手:

  1. 客户端发送 SYN 数据包给服务器

  2. 服务器收到客户端的数据包,返回 SYN/ACK 数据包给客户端

  3. 客户端收到服务器的返回后,发送 ACK 数据包给服务器,代表握手结束,连接成功

向服务器发起请求,返回结果

当连接成功后,可以正式向服务器发起请求了,如果这个请求是第一次访问该服务器,那么请求头里面不会带上缓存标识,直接请求服务器返回最新内容,但服务器如有在返回报文里面告诉客户端需要缓存这次的响应数据,这个时候有可能是强缓存标识也有可能是协商缓存标识,如果是强缓存,则下一次再访问的时候则如我们开头所说的,先从缓存里面校验返回;

我们接下来继续说的是,协商缓存,第二次请求头中带有浏览器上一次请求该资源的时间和一个资源校验码(使用资源修改时间、资源大小等信息生成),服务器收到这个请求后会判断协商缓存是否过期,如果过期则返回新的资源信息,如果没过期则返回 304 状态码,表示资源未更新,可以使用缓存中的资源。

浏览器解析 HTML

当服务接收到我们请求 baidu.com 给我们返回 HTML 文件时,结果为例子,浏览器是如何将 HTML 的内容渲染出来呈现给用户看的呢?

浏览器接收到服务器返回的 HTML 代码后,需要对其进行解析,浏览器使用状态机做词法分析,HTML 中有规定了 80 种状态,对接收的字符逐一判断,将可匹配成功的字符拆成独立状态,然后把所有独立状态的字符合并起来,形成一个联通图,通过词法分析将字符流解析出词来,然后要把这些词构建成 DOM 树,需要借助栈,通过语法分析对元素进行解析入栈,最后构建成 DOM 树,而浏览器会尽量流式自上而下地处理整个过程,每次解析出一个元素时,都会去匹配它的样式规则,然后根据规则的优先级,进行覆盖与调整,从而生成 CSS 规则树,这也是为什么我们的 CSS 选择器为什么没有子父选择器的原因,这也是说明了,CSS 解析过程并不会阻塞 DOM 的解析。那哪一块会引起 DOM 的解析?

HTML 代码中往往会引入一些额外的资源,比如说图片、CSS、JS等,图片和CSS 这些一般需要网络下载或者从缓存中读取,这些资源通常不会阻塞 HTML 的解析,因为他们不会影响 DOM 树的生成,但当 HTML 解析过程中遇到 script 标签,就会停止 HTML 的解析,转而去加载执行 JS 脚本,这是因为浏览器不知道这个 JS 的执行会不会改变当前的 HTML 结构,这也就是为什么 JS 脚本一般放在底部或者 加上 async 或 defer 属性异步加载 JS 的原因。(图片参考自极客时间 winter 的重学前端 )

浏览器布局渲染

当  HTML 解析完成后,我们可以得到一棵 DOM 树和一棵 CSS 规则树,这个时候要合并成一棵渲染树,为了构建渲染树,浏览器大致完成:

  1.  从 DOM 树根节点开始遍历每个可见节点

  2. 给每个可见节点匹配到其 CSS 规则

  3. 连同规则一起构建渲染树

有了渲染树后,就开始布局,到目前为止,我们对这棵渲染树只知道节点是哪些,样式有哪些,但并不知道在客户端可视窗口的哪个位置和大小,这个就是我们要提到的布局,也就是自动重排。为了了解到元素的确切位置,浏览器重根节点开始遍历,所有元素都会以绝对像素为单位被计算好在视区的确切位置以及其尺寸。

这个时候,我们就可以将渲染树上的每个节点都换成设备上的实际像素了。这一步通常叫做绘制或者栅格化。这里就要提到一个点:为什么要减少执行 DOM 操作,因为 DOM 操作会引起重排,因为每次的触发重排都会引起绘制。

 

参考:

猜你喜欢

转载自blog.csdn.net/vipshop_fin_dev/article/details/112503799
今日推荐