浏览器输入一个 URL 并按下 Enter 时,到底发生了什么?

0 阅读5分钟

这是个经典面试问题了,大家或多或少都能讲出来一些。我在最近的面试中,也遇到了这个问题,但有些细节忘记了,所以决定整理一下,也分享给大家,一起加油啦。


回答之前,要先有个整体思路:

  1. 先能连上机器:域名 → DNS 得到 IP
  2. 再能可靠传数据:与服务器端口建 TCP;若是 HTTPS,在 TCP 上再做 TLS
  3. 再谈要什么东西:发 HTTP 请求,收 HTML 等资源
  4. 最后才是看见页面:渲染引擎 解析、布局、绘制、合成

按顺序详细展开:

第 1 步:DNS——把域名变成 IP

网络层寻址只认 IP,不认域名;没有 IP,TCP 连谁都不知道。

发生了什么:

  • 先查缓存链(由近到远:浏览器 → 操作系统 → 路由器/本地 → ISP)。命中就直接拿到 IP。
  • 都不命中再走递归解析:根 DNS → 顶级域(如 .com)→ 权威 DNS,直到拿到该域名的 A/AAAA 记录。结果会回填缓存,下次更快。

DNS 是「分布式数据库 + 多级缓存」。

image.png

有了 IP + 端口,客户端才能和服务器建立 TCP 连接。


第 2 步:TCP——先有一条可靠的传输通道

HTTP 需要可靠、有序的字节流;这一层由 TCP 提供。

发生了什么: 三次握手——客户端发 SYN,服务器回 SYN-ACK,客户端再 ACK;双向确认、同步初始序列号,连接建立后才传应用数据。(部分场景首包可捎带数据。)

为什么要三次? 既要确认对端能收,也要确认本端发出的包对端能收到,并避免历史连接干扰。

sequenceDiagram
  participant C as 客户端
  participant S as 服务器
  C->>S: SYN
  S->>C: SYN-ACK
  C->>S: ACK(可带请求)

若是 http://,TCP 建好后就可以直接发 HTTP;若是 https://,还要先在 TCP 上完成 TLS,再发 HTTP


第 3 步(可选):TLS——HTTPS 下的加密

解决什么问题: 防窃听、防篡改、验证站点身份(证书)。

发生了什么: 在已建立的 TCP 上执行 TLS 握手:协商算法、校验证书链、协商对称密钥;之后 HTTP 内容主要走对称加密

通道与加密都就绪,才发送真正的 HTTP 请求


第 4 步:HTTP——要文档、拿响应

告诉服务器要什么资源,并取回字节内容

发生了什么:

  • 浏览器发 HTTP 请求,这个请求包含了⽅法(GET、POST等)、URI(统⼀资源标识符)和协议版本,以及可能包含的请求头和请求体;
  • 服务器返回状态码、响应头、响应体(通常是 HTML)。
  • HTML 里再引用 CSS、JS、图片,会触发更多请求

HTML 字节到手,才进入浏览器侧的解析与渲染


第 5 步:解析——[从 HTML/CSS/JS] => [DOM 与 CSSOM]

把文本变成浏览器能计算样式与布局的内部结构。

发生了什么:

  • 解析 HTML 得到 DOM 树
  • 遇到 CSS(如 <link>)去下载并解析成 CSSOM
  • 遇到 JS:默认会阻塞 DOM 解析(除非 defer/async 等)。

DOM + CSSOM 都参与构建渲染树
CSS 不阻塞 DOM 解析,但会阻塞渲染(没有样式,通常无法完成最终排版呈现)。


第 6 步:渲染树 → Layout → Paint——从结构到像素

发生了什么(顺序不能乱):

  1. Render Tree:DOM 与 CSSOM 结合;不必显示的节点(如 display: none)一般不进渲染树。
  2. Layout(布局 / 回流):计算位置、大小等几何信息。
  3. Paint(绘制):把可见内容画成像素表达(文字、颜色、边框等)。
  4. Composite(合成):部分层可单独合成,用 GPU 做滚动/动画等;滥用分层会占显存。
flowchart TD
  HTML[HTML] --> DOM[DOM]
  CSS[CSS] --> CSSOM[CSSOM]
  DOM --> RT[Render Tree]
  CSSOM --> RT
  RT --> L[Layout]
  L --> P[Paint]
  P --> CP[Composite]

之后若 DOM/样式变化,可能触发回流或仅重绘(只外观变)。
优化思路:减少回流、合并读写(DocumentFragment)、合理使用 transform/opacity 等,走合成路径。


我是这么回答的

按下 Enter 后,浏览器先处理 URL 和策略,看能不能复用连接;然后必须把域名解析成 IP,所以走 DNS,先查询各级缓存,没有就走递归解析,最终找到IP地址,就可以通过该IP地址去连接服务器,并且IP地址信息会被发送回⽤户的电脑,缓存起来。

有了 IP,就和服务器建立 TCP连接,三次握手。如果是 HTTPS,再在 TCP 上做 TLS 握手验证书、协商密钥。

通道好了就可以发 HTTP 请求了,拿到 index.html,再请求 CSS、JS 等。渲染引擎解析 HTML 成 DOM,解析 CSS 成 CSSOM,合并成 Render Tree,再 Layout 算布局,Paint 绘制,必要时 Composite 合成。

后面样式或 DOM 变化可能带来回流和重绘,开发上会注意减少回流、脚本用 defer/async 等。真实其他还有很多预解析、缓存、多路复用等优化,但主线就是这样。

追问:

  • defer和async属性在script标签中分别有什么作⽤?

script标签的 defer 和 async 属性⽤来控制外部脚本⽂件的加载和执⾏⽅式,它们对于改善⻚⾯加载速度⾮ 常有帮助。但是它们的机制并不相同:

defer的下载不会阻⽌DOM的构建,但是在DOM Tree构建完成后,在DOMContentLoaded事件之前,先执 ⾏脚本的内容,并且defer脚本的执⾏是有有序的。

async的下载也不会阻⽌DOM的加载,⽽且不会保证在DOMContentLoaded之前或者之后执⾏,也不能保证 顺序,它的每个脚本是独⽴进⾏的。

所以它们的应⽤场景是这样的: defer通常⽤于需要在⽂档解析后操作DOM的JavaScript代码,并且对多个script⽂件有顺序要求的;

async通常⽤于独⽴的脚本,对其他脚本,甚⾄对DOM没有依赖的脚本;

在现代化框架开发过程中,往往不需要我们⾃⼰来配置async或者defer,在使⽤脚⼿架或者⾃⼰搭建的webapck或 者vite项⽬进⾏打包时,它会根据需要帮我们加上defer属性,某些情况下我们想要进⾏性能优化时,也可以⼿动的 加上async属性(例如⼀些第三⽅的分析⼯具或者⼴告追踪脚本)。

  • 什么情况下引起回流呢?

DOM结构发⽣改变;

布局改变(修改了width、height、padding等值)

窗⼝resize(修改了窗⼝的尺⼨等)

调⽤getComputedStyle⽅法获取尺⼨、位置信息等;

总结

还有其他一些细节问题,就不展开啦。希望本文能够帮助到大家,祝商祺~