精校版:浏览器从输入URL到页面加载的全过程

发布于:2024-05-10 ⋅ 阅读:(25) ⋅ 点赞:(0)

这是一个经典前端面试题,并且一道题打通了很多知识点的联系。

本文所写的只是这道题的最优通用答法,如果你的面试官针对该过程中的特定部分提出更深入的问题拓展,欢迎在评论区分享你的见解,帮助掘友们打开视野,👏欢迎大家交流和讨论。

giphy (2).gif

全流程如下:

  1. URL 解析

  2. 缓存判断

  3. DNS 查询

  4. 建立 TCP 连接(三次握手)

  5. 发送 HTTP 请求

  6. 服务器处理请求并返回 HTTP 响应

  7. 浏览器解析渲染页面

    • 接收到响应后,开始解析 HTML 文件,构建 DOM 树
    • 资源加载,如 CSS、Javascript 文件、图片、视频等
    • 获取到所有并解析完成后,渲染页面,将结果显示给用户
  8. 断开 TCP 连接(四次挥手)


一、解析URL

分析 URL 所需要使用的传输协议和请求的资源路径。以下面 URL 为例

https://www.yuque.com/cascading:80/me1zy8/dy4lx3uq85ogtho3?id=110?testID=5&ID=123456&page=1#c3318eaa

一个完整的URL包括以下几部分:

  • 协议:HTTP、HTTPS
  • 域名:也可以使用 IP 地址作为域名使用,域名就是 IP 地址方便人记忆的写法
  • 端口(非必需):省略则使用默认的。HTTP 默认端口是 80,HTTPS 默认端口是 443
  • 虚拟目录(非必需):从域名后第一个 "/" 开始到最后一个 "/" 为止
  • 文件名(非必需):从域名后的最后一个 "/" 开始到 "?" 为止,若无问号则到 "#" 位置
  • 参数(非必需):"?" 开始到 "#" 结束,用于传参和接口查询,多个参数之间用 "&" 作为分隔符
  • 锚点(非必需):页面定位的地方

二、浏览器缓存判断

浏览器缓存是一种在本地存储网页文件(如 HTML 页面、图片、JavaScript 文件等)的技术,目的是减少加载网页的时间,降低服务器负载,提高用户体验。

  1. 浏览器首先检查本地缓存中是否有请求的资源。
  2. 如果有,浏览器会检查资源的有效性。通过 Cache-ControlExpires 来检查是否命中强缓存,命中则直接从缓存中读取资源,不发起请求。
  3. 如果没有命中强缓存,此时会暂停缓存查找,等后续 DNS 解析与建立 TCP 连接后,再向服务器发起 HTTP 请求,浏览器通过 EtagLast-Modify 来与服务器确认返回的响应资源是否被更改(协商缓存),若无更改则返回状态码(304 Not Modified),此时浏览器取本地缓存。
  4. 若协商缓存也没命中,则返回请求的具体数据。

注意:没有命中强缓存的时候,浏览器会暂停缓存的查找 。当通过 DNS 拿到 IP 地址,建立 TCP 连接,再发起 HTTP 请求的时候,会再来校验协商缓存。

相关字段解释

  • Expires HTTP/1.0 协议响应头部字段,设置一个绝对时间,告诉浏览器在此之前的都可以直接使用本地缓存,不用发请求。
# Expires 是一个日期和时间
# 这表示该响应将在 2015 年 10 月 21 日的 07:28:00 GMT 后过期。

Expires: Wed, 21 Oct 2015 07:28:00 GMT
  • Cache-Control HTTP/1.1 协议请求和响应头部字段,通过多命令精细化控制缓存行为:比如 max-age:缓存多久(秒)、no-store:不能缓存,用于敏感数据。

  • Last-Modify HTTP/1.0 协议响应头部字段,包含资源最后被修改的时间,精度到秒。
# 它的值是一个日期和时间
# 这表示该资源最后一次被修改的时间是 1994 年 11 月 15 日的 12:45:26 GMT。

Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
  • ETag HTTP/1.1 协议响应头部字段,不存时间,而是基于资源内容生成的一段哈希值,比 Last-Modify 更精确,因此若二者同时出现,ETag 优先级更高。
# 这表示该资源的版本标识是 "686897696a7c876b7e"。
# 当资源发生变化时,ETag 也会相应地改变。客户端可以使用 If-None-Match 头提供 ETag 值,以检查资源是否已更改。
# 若资源未更改,服务器将返回状态码 304 Not Modified
# 若资源已更改,服务器将返回新的资源和新的 ETag 值

ETag: "686897696a7c876b7e"

三、DNS解析

DNS(Domain Name System) 是域名和 IP 地址相互映射的一个分布式数据库。

域名分层结构:….三级域名.二级域名.顶级域名

若第二步,没本地缓存或者缓存资源过期,则浏览器接下来执行 DNS 解析动作。浏览器会:

  1. 先检查浏览器缓存中是否有访问网页的 IP 地址。

  2. 若浏览器缓存没有则会去 hosts 文件中找。

  3. 再没有,就去路由器缓存中查询。

  4. 都没有,浏览器才回去询问本地域名服务器

  5. 这个服务器一般是由 ISP 通信运营商提供,不属于树状结构内。若也没有,则它会开启递归查询过程:

    1. 根域名服务器: 根服务器不存具体域名和IP地址,它存负责特定顶级域的服务器地址。
    2. 顶级域名服务器: 引导指向查询权威服务器地址。
    3. 权威域名服务器: 最终提供准确的 IP 地址。

找到之后,本地DNS服务器会把这个域名- IP 地址的映射缓存,供后续使用,并将最终的 IP 地址返回给发起查询的浏览器。

四、TCP 三次握手

当浏览器通过 DNS 解析获取到服务器的 IP 地址后,它会开始建立一个 TCP 连接。

这个过程通常被称为"三次握手",具体步骤如下:

  1. SYN: 浏览器向服务器发送一个 SYN 包,表示希望建立连接。这个包中包含一个随机的序列号 A。
  2. SYN-ACK: 服务器收到 SYN 包后,返回一个 SYN-ACK 包,表示同意建立连接。这个包中包含一个自己的随机序列号 B,以及对客户端序列号 A 的确认(即 A + 1)。
  3. ACK: 浏览器收到 SYN-ACK 包后,再发送一个 ACK包,确认已经收到了服务器的 SYN-ACK 包。这个包中包含对服务器序列号 B 的确认(即 B + 1)。

五、发送 HTTP 请求

当 TCP 连接建立后,浏览器就可以开始发送 HTTP 请求了。一个 HTTP 请求主要包含以下几个部分:

  • 请求行:包含 HTTP方法(如GET、POST等)、请求的 URL 和 HTTP 版本。
  • 请求头:包含一些描述请求的元数据,如 User-Agent(描述浏览器类型)、Accept(描述浏览器接受的响应类型)、Connection(如 keep-alive 表示持久连接)等。
  • 空行:请求头和请求体之间的一个空行,表示请求头的结束。
  • 请求体:对于一些方法(如 POST、PUT 等),请求体中包含了要发送给服务器的数据。
    • 对于 GET 方法,请求体通常为空。

典型的 HTTP 请求可能看起来像这样:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive

这个请求使用了 GET 方法,请求的 URL 是 /index.html,HTTP 版本是 1.1。

请求头中包含了一些描述请求的信息,如 Host(服务器的域名)、User-Agent(浏览器类型)等。

POST /api/users HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Content-Type: application/json
Accept: application/json
Content-Length: 81
Connection: keep-alive

{
  "name": "John Doe",
  "email": "john.doe@example.com",
  "password": "secret"
}

这个请求使用了 POST 方法,上面的属性基本跟 GET 类似。

新属性 Content-Length 头部表示请求体的长度。空行后是请求体,包含了要发送给服务器的 JSON 数据。当服务器收到这个请求后,会处理请求,然后返回一个 HTTP 响应。

六、服务器处理请求并返回 HTTP 报文

当服务器收到 HTTP 请求后,它会根据请求的内容进行处理,并返回一个 HTTP 响应。一个 HTTP 响应主要包含以下几个部分:

  • 状态行:包含 HTTP 版本、状态码和状态文本。状态码是一个三位数,表示请求的处理结果。例如:
    • 200 表示请求成功
    • 304 表示资源未被改动
    • 404 表示请求的资源未找到
  • 响应头:包含一些描述响应的元数据,如:
    • Content-Type(响应体的类型)
    • Content-Length(响应体的长度)
    • Set-Cookie(设置cookie)等。
  • 空行:响应头和响应体之间的一个空行,表示响应头的结束。
  • 响应体:包含服务器返回的数据。这些数据可以是 HTML 文档,也可以是 JSON 数据,或者其他类型的数据。

一个典型的 HTTP 响应可能看起来像这样:

HTTP/1.1 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
Content-Type: text/html; charset=UTF-8
Content-Encoding: UTF-8
Content-Length: 138
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
ETag: "3f80f-1b6-3e1cb03b"
Accept-Ranges: bytes
Connection: close

<html>
  <head>
    <title>An Example Page</title>
  </head>
  <body>
    Hello World, this is a very simple HTML document.
  </body>
</html>

在这个响应中:

  • 状态行表示请求成功(200 OK)。
  • 响应头中包含了一些描述响应的信息,如 Date(响应的日期和时间)、Content-Type(响应体的类型,这里是text/html)等。
  • 响应体中包含了服务器返回的 HTML 文档,这个文档会被浏览器解析并显示给用户。

七、浏览器解析渲染页面

当浏览器接收到服务器的 HTTP 响应后,它会开始解析和渲染页面。这个过程包括以下几个步骤:

image-15.png

  1. 解析 HTML:浏览器首先解析 HTML 文档,构建 DOM 树。DOM 树是一个表示 HTML 文档结构的树形数据结构。
  2. 解析 CSS:浏览器解析 CSS 样式信息,构建 CSSOM 树。CSSOM 树是一个表示 CSS 样式信息的树形数据结构。
  3. 构建渲染树:浏览器将 DOM 树和 CSSOM 树合并,构建渲染树。渲染树表示了要在页面上显示的所有元素及其样式。
  4. 布局:浏览器计算渲染树中每个节点在页面上的位置,这个过程被称为布局或重排。
  5. 绘制:浏览器根据渲染树和布局信息,将每个节点绘制到屏幕上,这个过程被称为绘制或重绘。
  6. 加载 JavaScript:在这个过程中,浏览器还会加载和执行 JavaScript 代码。JavaScript 代码可以修改 DOM 树和 CSSOM 树,从而改变页面的内容和样式。

需要注意的是,这个过程并不是线性的,而是一个循环的过程。

例如,当 JavaScript 代码修改了 DOM 树或 CSSOM 树后,浏览器需要重新构建渲染树,重新进行布局和绘制。这就是所谓的重流和重绘。

八、断开TCP连接

当 HTTP 请求和响应的交互完成后,浏览器和服务器就可以断开 TCP 连接了。

这个过程通常被称为"四次挥手",具体步骤如下:

  1. FIN: 浏览器发送一个 FIN(finish)包,表示它已经完成了数据的发送。
  2. ACK: 服务器收到 FIN 包后,返回一个 ACK(acknowledge)包,确认已经收到了 FIN 包。
  3. FIN: 服务器完成对请求的处理后,也发送一个 FIN 包,表示它已经完成了数据的发送。
  4. ACK: 浏览器收到 FIN 包后,返回一个 ACK 包,确认已经收到了 FIN 包。然后浏览器等待一段时间,确保服务器已经收到了 ACK 包,然后关闭连接。

这个过程完成后,TCP 连接就被成功关闭了。

需要注意的是,这个过程中的每一个步骤都可能失败,例如,包可能会丢失,或者服务器可能会拒绝关闭连接。如果发生这种情况,浏览器会尝试重新发送 FIN 包,或者报告错误。


网站公告

今日签到

点亮在社区的每一天
去签到