请完整描述从输入网址到浏览器渲染出页面,发生了什么?
这是一道非常经典且能考察前端基本功深度和广度的面试题。为了把这个过程讲透,我们需要从网络协议层到浏览器内核渲染层进行逐一拆解。
以下是从输入 URL 到页面最终呈现的完整链路:
第一步:URL 解析与缓存判断
-
解析 URL:浏览器会首先分析输入的字符串。提取出协议(如
https)、域名(如www.example.com)、端口(默认 HTTPS 为 443)、路径和参数。 -
HSTS 检查:如果网站启用了 HSTS(HTTP 严格传输安全),浏览器会强制将
http转换为https。 -
缓存检查:浏览器会检查是否有本地缓存。
-
首先检查 Service Worker(如果注册过)。
-
接着检查 Memory Cache(内存缓存,如页面上加载过的图片)。
-
最后检查 Disk Cache(硬盘缓存,检查 HTTP 响应头中的强缓存字段
Expires和Cache-Control)。如果强缓存有效,直接返回资源,结束网络请求阶段;如果失效或需协商缓存,则进入下一步。
-
第二步:DNS 域名解析
由于网络传输需要目标服务器的 IP 地址,而不是域名,因此需要通过 DNS 将域名翻译成 IP 地址。
解析过程是分层查找的:
-
本地缓存:浏览器缓存 -> 操作系统缓存(如
hosts文件) -> 路由器缓存。 -
本地域名服务器 (Local DNS):如果本地没有,会向网络运营商 (ISP) 提供的 DNS 服务器发起请求。
-
迭代查询:如果 Local DNS 也没有,它会向上级发起迭代查询:
-
根域名服务器(返回顶级域名
.com的地址)。 -
顶级域名服务器(返回权威域名
example.com的地址)。 -
权威域名服务器(返回最终的 IP 地址)。
-
第三步:建立 TCP 连接(三次握手)
拿到 IP 地址后,浏览器会利用底层的 TCP/IP 协议族与服务器建立可靠的连接。TCP 通过“三次握手”来确保双方都有发送和接收的能力。
-
第一次握手 (SYN):客户端生成一个随机序列号
seq = x,并发送一个 SYN 标志位为 1 的数据包给服务器。(证明客户端有发送能力) -
第二次握手 (SYN + ACK):服务器收到后,回应一个 SYN 为 1、ACK 为 1 的数据包。包含服务器的随机序列号
seq = y,以及确认号ack = x + 1。(证明服务器有接收和发送能力) -
第三次握手 (ACK):客户端收到后,发送一个 ACK 为 1 的数据包,
ack = y + 1,seq = x + 1。(证明客户端有接收能力)至此,双方的收发能力都得到了验证,全双工连接建立。
第四步:TLS/SSL 协商(HTTPS 加密建立)
因为 HTTP 是明文传输,极不安全。HTTPS 在 HTTP 和 TCP 之间加入了 TLS/SSL 层。建立 TCP 连接后,还需要进行 TLS 握手来生成对称加密的密钥。
-
Client Hello:客户端发送支持的 TLS 版本、加密套件列表和一个客户端随机数 (Random1)。
-
Server Hello:服务器确认 TLS 版本和加密套件,发送数字证书(含服务器公钥)和一个服务端随机数 (Random2)。
-
证书验证:客户端通过操作系统内置的 CA(证书颁发机构)根证书,验证服务器证书的合法性(是否过期、是否被篡改等)。
-
生成预主密钥 (Pre-Master Secret):验证通过后,客户端生成第三个随机数**(Pre-Master Secret)**,并用服务器的公钥加密后发送给服务器。
-
生成会话密钥:服务器用自己的私钥解密出 Pre-Master Secret。此时,客户端和服务器都拥有了三个随机数。双方使用相同的算法,将这三个随机数计算生成最终的**“对称密钥”**(Session Key)。
此后的所有 HTTP 通信,都将使用这个对称密钥进行高效率的加解密。
第五步:发送 HTTP 请求与接收响应
-
构建请求:浏览器根据规范构建 HTTP 请求报文(包含请求行、请求头、请求体),并加上之前提到的协商缓存字段(如
If-Modified-Since或If-None-Match)。 -
服务器处理:请求到达服务器(通常先经过负载均衡、Nginx 反向代理等),后端应用程序处理业务逻辑,可能还会查询数据库。
-
返回响应:服务器组装 HTTP 响应报文返回。
-
如果返回状态码 304 (Not Modified),说明协商缓存生效,浏览器直接使用本地缓存。
-
如果返回状态码 200 (OK),说明请求成功,响应体会携带请求的数据(通常是 HTML 文本)。
-
第六步:浏览器解析与渲染
这是前端开发者最关心的阶段。浏览器内核(如 Chrome 的 Blink 和 V8 引擎)拿到 HTML 文件后,开始将其转化为屏幕上的像素。
-
构建 DOM 树 (DOM Tree):
- HTML 解析器将字节流转换为字符,再进行词法分析(Tokenize),最终构建出树状的 Document Object Model (DOM)。
-
构建 CSSOM 树 (CSSOM Tree):
-
遇到 CSS 资源(
<link>或<style>),会并行发起请求获取(不会阻塞 DOM 解析,但会阻塞渲染)。 -
解析 CSS 生成 CSS Object Model (CSSOM) 树。
-
-
执行 JavaScript:
- 遇到
<script>标签,HTML 解析会暂停(除非设置了async或defer属性),等待 JS 下载并执行完毕,因为 JS 有可能修改 DOM 或 CSSOM。
- 遇到
-
生成渲染树 (Render Tree):
-
将 DOM 树和 CSSOM 树合并。遍历 DOM 树的可见节点,找到对应的 CSSOM 规则并应用。
-
注意:
display: none的元素不在 Render Tree 中,但visibility: hidden的元素会在。
-
-
布局 / 重排 (Layout / Reflow):
- 根据 Render Tree,计算每个节点在屏幕上的确切几何位置和大小。这是个非常消耗性能的过程。
-
绘制 / 重绘 (Paint / Repaint):
- 遍历 Render Tree,调用操作系统的底层绘图 API,将每个节点转化为屏幕上的实际像素(包括颜色、阴影、文本等)。
-
分层与合成 (Compositing):
-
对于复杂的页面,浏览器会将页面分为多个图层(Layer),比如包含
transform: translateZ(0)、video、canvas的元素会被提升为独立图层。 -
GPU 介入,将这些图层按正确顺序组合在一起,最终显示在屏幕上。
-
第七步:断开连接(四次挥手)
当数据传输完毕,且 HTTP 请求头中没有设置 Connection: keep-alive(或者达到了 Keep-Alive 的超时时间),TCP 连接会被断开。由于 TCP 是全双工的,因此需要双方分别断开自己的发送通道。
-
第一次挥手 (FIN):主动方(通常是客户端,也可能是服务器)发送 FIN 报文,表示“我没有数据要发送了,我要断开连接”。
-
第二次挥手 (ACK):被动方收到 FIN,回复 ACK。此时主动方到被动方的方向断开,处于“半关闭”状态(被动方如果还有数据没发完,还可以继续发)。
-
第三次挥手 (FIN):被动方的数据也发送完毕了,发送 FIN 报文给主动方,表示“我也没数据要发了,准备断开”。
-
第四次挥手 (ACK):主动方收到 FIN,回复 ACK。被动方收到后立即关闭连接;主动方则进入 TIME_WAIT 状态,等待 2MSL(报文最大生存时间)后,确保对方收到了 ACK,才真正关闭连接。
这个流程涵盖了前端工程师在日常开发和性能优化(如减少重排重绘、利用缓存、减少网络请求体积、理解异步脚本加载)中需要建立的底层认知体系。