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

2025年 阅读约 12 分钟 面试指南 · 计算机网络

深入解析浏览器输入URL到页面展示的完整过程:URL解析、DNS解析、TCP连接、TLS握手、HTTP请求响应、浏览器渲染流程(DOM/CSSOM/布局/绘制)、重排重绘优化,附面试模拟问答。

一句话总结

完整流程分两大阶段:网络阶段渲染阶段。网络阶段:URL 解析 → DNS 解析(域名→IP)→ TCP 三次握手 → TLS 握手(HTTPS)→ 发送 HTTP 请求 → 服务端处理 → 返回 HTTP 响应。渲染阶段:解析 HTML → 构建 DOM 树 → 解析 CSS → 构建 CSSOM 树 → 合并为渲染树 → 布局(Layout/Reflow)→ 绘制(Paint)→ 合成(Composite)→ 显示。关键优化:避免重排(改宽高/位置),减少重绘(改颜色),利用合成(transform/opacity)。

初级理解

完整流程概览

# 输入 https://www.example.com 后的完整过程: # === 网络阶段 === # 1. URL 解析 # 判断是 URL 还是搜索关键词 # 解析协议(https)、域名(www.example.com)、路径(/) # 2. DNS 解析 # 浏览器缓存 → OS 缓存 → hosts → DNS 服务器 # 域名 → IP 地址(93.184.216.34) # 3. TCP 三次握手 # SYN → SYN+ACK → ACK # 建立客户端与服务端的可靠连接 # 4. TLS 握手(HTTPS) # Client Hello → Server Hello + 证书 → 密钥交换 # 建立加密通道 # 5. 发送 HTTP 请求 # GET / HTTP/1.1 # Host: www.example.com # ... # 6. 服务端处理 # Nginx 接收 → 转发到应用服务器 → 业务处理 # → 返回 HTML # 7. 返回 HTTP 响应 # HTTP/1.1 200 OK # Content-Type: text/html # <html>...</html> # === 渲染阶段 === # 8. 解析 HTML → DOM 树 # 9. 解析 CSS → CSSOM 树 # 10. DOM + CSSOM → 渲染树(Render Tree) # 11. 布局(Layout):计算每个元素的位置和大小 # 12. 绘制(Paint):填充像素 # 13. 合成(Composite):分层合并显示

中级深入

浏览器渲染详细流程

# 1. 解析 HTML → DOM 树 # 字节 → 字符 → Token → 节点 → DOM 树 # 遇到 <script> 阻塞 DOM 解析(除非 async/defer) # 遇到 <link> 不阻塞 DOM 解析,但阻塞渲染 # 2. 解析 CSS → CSSOM 树 # 浏览器 CSS 默认样式 + 外部 CSS + 内联样式 # CSS 解析不阻塞 DOM 构建,但阻塞渲染 # (因为 CSSOM 没构建完,渲染树无法生成) # 3. DOM + CSSOM → 渲染树 # 只包含可见元素(display:none 不在渲染树中) # 每个可见节点匹配对应的 CSS 规则 # 4. 布局(Layout / Reflow) # 计算每个节点的几何信息(位置、大小) # 从根节点开始递归计算 # 触发条件:DOM 变化、样式变化、窗口大小变化 # 5. 绘制(Paint) # 将渲染树转换为屏幕上的像素 # 生成绘制指令列表 # 6. 合成(Composite) # 将多个图层合并为最终画面 # transform/opacity 只触发合成,不触发重排重绘

重排(Reflow)vs 重绘(Repaint)

# 重排(Reflow):重新计算布局 # 触发条件: # 1. 添加/删除 DOM 元素 # 2. 修改元素尺寸(width/height/padding/margin/border) # 3. 修改元素位置(position/top/left) # 4. 修改字体大小 # 5. 改变窗口大小 # 6. 读取某些属性(offsetWidth/scrollTop 等) # 重绘(Repaint):重新绘制像素 # 触发条件: # 1. 修改颜色(color/background-color) # 2. 修改可见性(visibility) # 3. 修改阴影(box-shadow) # 性能影响:重排 > 重绘 > 合成 # 重排一定触发重绘,重绘不一定触发重排 # 优化建议: # 1. 批量修改 DOM(DocumentFragment) # 2. 使用 class 替代逐个修改 style # 3. 避免频繁读取布局属性 # 4. 动画使用 transform/opacity(只触发合成) # 5. 使用 requestAnimationFrame

高级进阶

script 标签的 async 和 defer

# 普通 script(无属性): # HTML 解析到 script → 暂停解析 → 下载并执行 JS # → 继续解析 HTML # 阻塞 DOM 构建 # <script async>: # HTML 解析和 JS 下载并行 # JS 下载完立即执行(暂停 HTML 解析) # 执行顺序不确定(谁先下载完谁先执行) # 适用:独立脚本(如统计、广告) # <script defer>: # HTML 解析和 JS 下载并行 # JS 在 HTML 解析完成后、DOMContentLoaded 前执行 # 执行顺序确定(按文档顺序) # 适用:依赖 DOM 的脚本 # 总结: # 普通:下载+执行都阻塞 # async:下载不阻塞,执行阻塞,顺序不确定 # defer:下载不阻塞,执行不阻塞,顺序确定

关键渲染路径优化

# 关键渲染路径(Critical Rendering Path): # 从收到 HTML 到首次渲染的步骤 # 优化策略: # 1. 减少关键资源数量 # - 内联关键 CSS(首屏样式) # - 延迟加载非关键 CSS/JS # - 使用 media 属性(print 等) # 2. 减少关键字节数 # - 压缩 HTML/CSS/JS(Gzip/Brotli) # - 代码分割(Code Splitting) # - Tree Shaking # 3. 缩短关键路径长度 # - 减少请求次数(合并文件) # - 使用 CDN # - 预加载关键资源: # <link rel="preload" href="font.woff2" as="font"> # <link rel="preconnect" href="https://api.example.com">

实战场景

# 场景1:首屏加载优化 # 1. 服务端渲染(SSR):直接返回渲染好的 HTML # 2. 骨架屏:先显示占位,数据到了再填充 # 3. 图片懒加载:<img loading="lazy"> # 4. 路由懒加载:按需加载 JS # 场景2:动画性能优化 # 错误做法(触发重排): element.style.left = '100px'; // 每帧都重排 element.style.top = '100px'; # 正确做法(只触发合成): element.style.transform = 'translate(100px, 100px)'; # transform 在合成线程执行,不阻塞主线程 # 场景3:强制同步布局问题 # 错误(读取布局属性触发强制重排): element.style.width = '100px'; console.log(element.offsetWidth); // 强制重排! element.style.height = '100px'; # 正确(先读后写): console.log(element.offsetWidth); // 先读 element.style.width = '100px'; // 后写 element.style.height = '100px';

面试模拟

面试官:从输入 URL 到页面展示,发生了什么?

你:分两大阶段。网络阶段:URL 解析 → DNS 解析 → TCP 三次握手 → TLS 握手(HTTPS)→ HTTP 请求 → 服务端处理 → HTTP 响应。渲染阶段:解析 HTML 构建 DOM → 解析 CSS 构建 CSSOM → 合并为渲染树 → 布局计算位置大小 → 绘制像素 → 合成显示。JS 会阻塞 DOM 解析,CSS 会阻塞渲染。

面试官:什么是重排和重绘?如何优化?

你:重排是重新计算布局(改尺寸/位置),重绘是重新绘制像素(改颜色)。重排一定触发重绘。优化:1)批量修改 DOM(DocumentFragment);2)用 class 替代逐个改 style;3)避免频繁读取布局属性;4)动画用 transform/opacity(只触发合成,性能最好);5)使用 requestAnimationFrame。