输入URL到页面展示发生了什么

2024/7/28 browser

image-20240728164234531

​ 从输入 URL 回车到页面展示,这中间究竟发生了什么呢?

从url到页面渲染

​ 这个过程可以大致描述为如下。

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

1、输入信息处理

2、DNS 查询

3、TCP 连接

4、发送请求

5、接受响应

6、渲染页面

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

# 1、输入信息处理

​ 处理输入信息,URL 解析。用户输入url并回车,浏览器进程检查url,组装协议,构成完整的url。

  • 检查用户输入

    ​ 当用户在地址栏中输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容,还是请求的 URL。根据输入内容进行自动完成URL合成,字符编码等,同时,还有安全检查、访问限制等操作。

    ① 如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL

    ② 如果判断输入内容符合 URL 规则,比如输入time.geekbang.org,那么地址栏会根据规则,把这段内容加上协议,合成为完整的 URL,比如https://time.geekbang.org/

当用户输入关键字并键入回车之后,浏览器当前的页面并没有被立即替换成新的页面,是为什么呢?

  • beforeunload 事件

    ​ 当用户输入关键字并键入回车之后,这意味着当前页面即将要被替换成新的页面,不过在这个流程继续之前,浏览器还给了当前页面一次执行 beforeunload 事件的机会,beforeunload 事件允许页面在退出之前执行一些数据清理操作,还可以询问用户是否要离开当前页面,比如当前页面可能有未提交完成的表单等情况,因此用户可以通过 beforeunload 事件来取消导航,让浏览器不再执行任何后续工作。

当前页面没有监听 beforeunload 事件或者同意了继续后续流程,那么浏览器便进入加载状态。

  • 浏览器进入加载状态

    ​ 当浏览器刚开始加载一个 URL 时,标签页上的图标便进入了加载状态。但此时浏览器中页面显示的依然是之前打开的页面内容,并没立即替换为新的页面。因为需要等待提交文档阶段,页面内容才会被替换。

# 2、进入真正的URL请求过程

​ 接下来,便进入了页面资源请求过程。这时,浏览器进程会把URL请求地址发送至网络进程,当网络进程收到URL之后,才会开始真正的URL请求流程

  • 发送到网络进程

    浏览器进程通过进程间通信(IPC)把 URL 请求发送给网络进程。

  • 检查缓存

    网络进程接收到 URL 请求后,会在这里发起真正的 URL 请求流程。

    首先,网络进程会检查本地缓存是否缓存了该请求资源。

    ① 如果有,那么直接将该资源返回给浏览器进程(浏览器缓存中的强制缓存);

    ② 如果没有,则网络进程向web服务器发起http请求,直接进入网络请求流程(DNS解析以获取域名的服务器IP地址;如果请求协议是HTTPS,还需要建立TLS连接)。

    这里涉及到浏览器缓存机制,具体翻阅 浏览器缓存 (opens new window)

# 3、DNS 查询

进行DNS解析,获取服务器 IP 地址

​ 对 URL 进行 DNS 解析,以获取请求域名的服务器 IP 地址和端口号。如果没有端口号,http 默认80,https 默认 443,如果请求协议是 HTTPS,那么还需要建立 TLS/SSL 连接。

这里涉及到 DNS 解析过程、DNS的递归与迭代查询,具体翻阅 DNS 域名系统 (opens new window)

DNS 解析域名的过程

  • 询问浏览器 DNS 缓存
  • 询问本地操作系统 DNS 缓存(即查找本地 host 文件)
  • 询问 ISPInternet Service Provider)互联网服务提供商(例如电信、移动)的 DNS 服务器
  • 询问根服务器,这个过程可以进行递归和迭代两种查找的方式,两者都是先询问顶级域名服务器查找

# 4、TCP 连接

​ 然后,利用 IP 地址和服务器建立 TCP 连接

​ 首先,判断是不是 https 的,如果是,则 HTTPS 其实是 HTTP + SSL / TLS 两部分组成,也就是在 HTTP 上又加了一层 处理加密信息的模块。服务端和客户端的信息传输都会通过 TLS 进行加密,所以传输的数据都是加密后的数据。

​ 进入 TCP 队列(单个域名 TCP 连接数量限制),通过 三次握手机制 与服务器建立连接。Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。如果当前请求数量少于 6 个,会直接建立 TCP 连接。

​ 这里涉及到 TCP 三次握手和四次挥手、HTTP2、TSL/SSL 等网络知识,具体翻阅 传输层 TCP 协议 (opens new window)等文章。

三次握手过程

  1. 客户端发送标有 SYN 的数据包,表示我将要发送请求。
  2. 服务端发送标有 SYN/ACK 的数据包,表示我已经收到通知,告知客户端发送请求。
  3. 客户端发送标有 ACK 的数据包,表示我要开始发送请求,准备被接受。

数据传输

  1. 服务端接收到数据包,并发送确认数据包已收到的消息到客户端,不断重复这个过程
  2. 客户端在发送一个数据包后,未接收到服务端的确定消息,则重新发送该数据包,即 TCP 的重发机制
  3. 当接收完所有的数据包后,接收端会按照 TCP 头中的需要进行排序,形成完整的数据

四次挥手过程

  1. 客户端发送请求,申请断开连接,进入等待阶段,此时不会发送数据,但是会继续接收数据。
  2. 服务端接收请求后,告知客户端已明白,此时服务端进入等待状态,不会再接收数据,但是会继续发送数据。
  3. 客户端收到后,进入下一阶段等待。
  4. 服务端发送完剩余的数据后,告a知客户端可以断开连接,此时服务端不会发送和接收数据。
  5. 客户端收到后,告知服务端我开始断开连接。
  6. 服务端收到后,开始断开连接。

# 5、发送请求

构建请求头信息,发送请求头信息。

​ 连接建立之后,浏览器端会构建请求行(方法、URL、协议)、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。

  • http 请求加上 TCP 头部——包括源端口号、目的程序端口号和用于校验数据完整性的序号,向下传输
  • 网络层在数据包上加上IP头部——包括源 IP 地址和目的 IP 地址,继续向下传输
  • 底层通过物理网络传输给目的服务器主机

# 6、接受响应

服务器响应后,网络进程接收响应头和响应信息,并解析响应内容。

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

  • 服务器主机网络层接收到数据包,解析出 IP 头部,识别出数据部分,将解开的数据包向上传输到传输层

  • 服务器主机传输层获取到数据包,解析出 TCP 头部,识别端口,将解开的数据包向上传输到应用层

  • 应用层 HTTP 解析请求头和请求体,检查状态码

    ① 如果需要重定向,直接返回HTTP响应数据的状态码 301/302,并在响应头的 Location 字段中附上重定向地址,浏览器会根据状态码和 Location进行重定向操作;

    ② 如果不是重定向,首先服务器会根据请求头中的 If-None-Match 的值来判断请求的资源是否被更新,如果没有更新,就返回 304 状态码,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了;否则,返回新数据和 200 状态码,并且如果想要浏览器缓存数据的话,就在相应头中加入字段:Cache-Control:Max-age=2000。响应数据又顺着应用层-传输层-网络层-网络接口层-网络接口层-网络层-传输层-应用层的顺序返回到网络进程。

是否断开连接

​ 数据传输完成,TCP 四次挥手断开连接。如果,浏览器或者服务器在 HTTP 头部加上Connection:Keep-Alive,TCP 就一直保持连接。保持 TCP 连接可以省下次需要建立连接的时间,提升资源加载速度。

重定向

​ 在接收到服务器返回的响应头后,网络进程开始解析响应头,如果发现返回的状态码是 301/302,那么说明服务器需要浏览器重定向到其他新的 URL 继续导航。这时网络进程会从响应头的 Location 字段里面读取重定向的地址,然后再发起新的 HTTP 或者 HTTPS 请求,一切又从头开始了。如果响应行是 200,那么表示一切正常,浏览器可以继续处理该请求。

响应数据类型处理

200 状态码响应处理检查响应类型Content-Type**,浏览器会根据HTTP头中Content-Type首部字段来判断服务器返回的响应体数据是什么类型,并根据类型决定如何显示响应体的内容。如果Content-Type字段的值被浏览器判断字节流类型(下载类型),则将该请求提交给浏览器的下载管理器,同时该URL请求的导航流程就此结束,不再进行后续的渲染。但如果是 HTML,浏览器则会继续进行导航流程,由于Chrome的页面渲染是运行在渲染进程中的,所以网络进程会通知浏览器进程准备渲染进程,准备进行渲染。

# 6、渲染页面

# 准备渲染进程

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

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

​ 浏览器进程会检查当前 URL 和之前打开的渲染进程根域名是否相同,如果相同则复用原来的进程,如果不同则开启新的渲染进程。

​ 当前需要渲染HTML时,则需要创建渲染进程,用于后期渲染HTML。而对于渲染进程,如果是同一站点是可以共享一个渲染进程,例如 a.abc.comc.abc.com 可以共享一个渲染渲染进程。否则,需要重新创建渲染进程。需要注意的是,同站指的是顶级域名二级域名相等

渲染进程准备好之后,还不能立即进入文档解析状态,因为此时的文档数据还在网络进程中,并没有提交给渲染进程,所以下一步就进入了提交文档阶段。

在创建完渲染进程后,网络进程会将接收到的 HTML、JavaScript 等数据传递给渲染进程。

# 提交文档

​ 所谓提交文档,就是浏览器进程将网络进程接收到的 HTML 数据提交给渲染进程,可以理解为需要等待渲染进程网路进程之间建立传输数据的”管道“,只有等文档数据传输完毕之后渲染进程才会返回一些页面重要的信息和状态给浏览器进程,才会更新当前页面。

​ 具体流程是这样的:

① 当浏览器进程接收到网络进程的响应头数据之后,浏览器会发出提交文档的消息给渲染进程

② 渲染进程接收到消息后,会和网络进程建立传输数据的管道

③ 等文档数据传输完成之后,渲染进程会返回确认提交的消息给浏览器进程(文档指的是 URL 请求的响应体)

注意

​ 网络进程向渲染进程提交文档,是边下载边传递给渲染进程解析的,接收到第一批数据,便开始做 DOM 解析了!即提交文档后便开始进行解析 DOM,解析 CSS,生成布局树,绘制等操作。

# 更新浏览器界面状态

​ ④浏览器进程在收到确认提交的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退(此时enable,显示正在加载状态)的历史状态,并更新 Web 页面。此时的 web 页面是空白页。

img

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

到这里,一个完整的导航流程就“走”完了,这之后就要进入渲染阶段了。

# 渲染文档

​ 一旦页面生成完成,渲染进程会发送一个消息给浏览器进程,浏览器接收到消息后,会停止标签图标上的加载动画

渲染进程对文档进行页面解析和子资源加载。

① HTML 通过 HTML 解析器转成 DOM Tree

由于网络进程传输给渲染进程的是 HTML 字符串,所以,渲染进程需要将 HTML 字符串转化成 DOM 树。

preview

② CSS 按照 CSS 规则和 CSS解释器转成 CSSOM TREE,两个 tree 结合,形成 render tree(不包含HTML的具体元素和元素要画的具体位置)

构建 CSSOM 的过程,即通过解析 CSS 文件、style 标签、行内 style 等,生成 CSSOM

preview

③ 通过 Layout 可以计算出每个元素具体的宽高颜色位置,结合起来,开始绘制,最后显示在屏幕中新页面显示出来

这里涉及到浏览器渲染流程,具体翻阅 浏览器渲染 (opens new window)

参考资料:

详细拆解导航流程:从输入URL到页面展示,这中间发生了什么?-腾讯云开发者社区-腾讯云 (tencent.com) (opens new window)

前端 - (详解)从浏览器输入 URL 到页面展示过程发生了什么? - 个人文章 - SegmentFault 思否 (opens new window)

上次更新: 2024/8/17 02:25:10