这是个经典面试问题了,大家或多或少都能讲出来一些。我在最近的面试中,也遇到了这个问题,但有些细节忘记了,所以决定整理一下,也分享给大家,一起加油啦。
回答之前,要先有个整体思路:
- 先能连上机器:域名 → DNS 得到 IP。
- 再能可靠传数据:与服务器端口建 TCP;若是 HTTPS,在 TCP 上再做 TLS。
- 再谈要什么东西:发 HTTP 请求,收 HTML 等资源。
- 最后才是看见页面:渲染引擎 解析、布局、绘制、合成。
按顺序详细展开:
第 1 步:DNS——把域名变成 IP
网络层寻址只认 IP,不认域名;没有 IP,TCP 连谁都不知道。
发生了什么:
- 先查缓存链(由近到远:浏览器 → 操作系统 → 路由器/本地 → ISP)。命中就直接拿到 IP。
- 都不命中再走递归解析:根 DNS → 顶级域(如
.com)→ 权威 DNS,直到拿到该域名的 A/AAAA 记录。结果会回填缓存,下次更快。
DNS 是「分布式数据库 + 多级缓存」。
有了 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——从结构到像素
发生了什么(顺序不能乱):
- Render Tree:DOM 与 CSSOM 结合;不必显示的节点(如
display: none)一般不进渲染树。 - Layout(布局 / 回流):计算位置、大小等几何信息。
- Paint(绘制):把可见内容画成像素表达(文字、颜色、边框等)。
- 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⽅法获取尺⼨、位置信息等;
总结
还有其他一些细节问题,就不展开啦。希望本文能够帮助到大家,祝商祺~