模块二 · 工程化与网络
从输入 URL 到页面渲染:完整链路
一道题串起 DNS、TCP、TLS、HTTP 与浏览器渲染。这一节按时间顺序拆解每一跳,并指出每一跳能优化什么。
读完这一节,你能做到
- 按顺序说出从输入 URL 到首屏的每一跳。
- 判断一次慢请求卡在 DNS、连接、服务端还是渲染。
- 说清关键渲染路径的五个步骤。
- 用首屏指标定位优化点。
一、URL 解析与缓存查找
浏览器先解析 URL:协议、域名、端口、路径、查询参数。然后按顺序查找缓存:
Service Worker → Memory Cache → Disk Cache → 强缓存 → 协商缓存 → 网络
强缓存看 Cache-Control/Expires,命中就不发请求;协商缓存看 ETag/Last-Modified,命中返回 304,省掉响应体。
这一跳最常见的优化是给静态资源加长缓存 + 内容哈希,把"重复下载"变成"直接命中"。
二、DNS 与连接
- DNS:域名解析成 IP。优化手段是 DNS 预解析(
dns-prefetch)与减少域名数量。 - TCP:三次握手建立连接。HTTP/1.1 有连接数限制,HTTP/2 多路复用,HTTP/3 用 QUIC 减少握手。
- TLS:HTTPS 需要 TLS 握手,会多出 1-2 个 RTT;会话复用与 TLS 1.3 能减少开销。
<link rel="dns-prefetch" href="//cdn.example.com" />
<link rel="preconnect" href="https://cdn.example.com" crossorigin />
预连接适合"确定马上要用"的域名,不要给所有域名都加,反而浪费连接。
三、HTTP 请求与响应
请求发出后,服务端处理并返回响应。需要关注:
| 部分 | 关注点 |
|---|---|
| 状态码 | 200/301/304/401/500 分别对应什么 |
| 响应头 | 缓存、压缩、CORS、安全策略 |
| 响应体 | HTML 大小、是否启用 gzip/brotli |
| 首字节 | TTFB 反映服务端与网络延迟 |
TTFB 高通常是服务端或网络问题,不是前端能解决的;这时优化前端资源没有意义。
四、浏览器进程模型
现代浏览器是多进程架构:
- 浏览器进程:地址栏、标签页管理。
- 渲染进程:每个标签页一个,负责 HTML/CSS/JS 执行与渲染。
- GPU 进程:合成与绘制。
- 网络进程:请求与缓存。
渲染进程内的主线程同时负责 JS、样式、布局与绘制,所以长任务会阻塞渲染——这也是"事件循环"和"性能优化"的连接点。
五、关键渲染路径
HTML → DOM
CSS → CSSOM
DOM + CSSOM → Render Tree → Layout → Paint → Composite
五个关键结论:
- CSS 是阻塞渲染的:没有 CSSOM,浏览器不会绘制。
- JS 是阻塞解析的(未加 async/defer 时):脚本会打断 HTML 解析。
- 布局与绘制在主线程,合成可以在合成线程。
transform/opacity通常只触发合成,成本低;改宽高会触发布局。- 图片要预留尺寸,否则会引起布局偏移(CLS)。
六、首屏指标与优化
| 指标 | 反映 | 优化方向 |
|---|---|---|
| TTFB | 服务端与网络 | 缓存、CDN、后端 |
| FCP | 首次内容绘制 | 阻塞资源、字体 |
| LCP | 最大内容绘制 | 图片、关键 CSS |
| TBT | 主线程阻塞 | 拆长任务、减少 JS |
| CLS | 布局偏移 | 预留尺寸 |
一个实用顺序:先看 Network 瀑布图找"最慢的一跳",再看 Performance 找长任务,最后才谈代码拆分。
一次慢首屏的排查案例
某后台首屏 5.2 秒。Network 显示:HTML TTFB 1.8 秒、一个同步脚本 1.1MB、首屏接口串行 1.2 秒。
处理顺序:
- 服务端加缓存,TTFB 降到 300ms。
- 脚本改 defer + 路由分包,阻塞时间降到 200ms。
- 首屏接口并行化,并加骨架屏。
结果 1.9 秒。代价是首屏多了一次加载态,且服务端需要维护缓存失效。
这个案例说明:首屏慢通常不是单一原因,要按“网络 → 资源 → 接口”的顺序拆。
关键请求链
浏览器会为关键资源排优先级:HTML 最高,CSS 次之,字体与首屏图片再次。preload 可以提升发现晚的关键资源,但会抢占带宽;prefetch 是空闲时预取下一页,优先级最低。
判断标准:这个资源是否“首屏必须且发现得晚”。是,才 preload。
关键渲染路径的两条优化线
第一条是“少阻塞”:CSS 内联关键部分、脚本加 defer、字体用 font-display: swap。
第二条是“早发现”:用 preload/preconnect 提前建立连接、提前拉取关键资源;用 103 Early Hints 让浏览器在服务端思考时就预加载。
两条线的目标相同:让首屏需要的东西更早到、更少排队。不要只做其中一条。
一次请求的耗时拆解
拿到瀑布图后,把一次请求拆成:排队 → DNS → 连接 → TLS → TTFB → 下载。哪一段最长,优化就落在哪:
- 排队/连接长:连接未复用、域名太多。
- TTFB 长:服务端或网络。
- 下载长:体积、压缩、带宽。
拆完再谈手段,比背清单更有效。
七、常见坑
- 所有资源都加 preload,反而抢了关键资源带宽。
- CSS 放底部或用 JS 注入,导致白屏时间变长。
- 同步脚本放 head,阻塞解析。
- 只优化包体,不优化 TTFB。
- 图片没有宽高,造成 CLS。
八、动手任务:自写 → AI 写 → 对比 → 取舍
四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。 完整动作与记录模板见《AI 协作与代码评审》。
① 自己写
- 用 Network 面板记录一次首屏,标出 DNS、连接、TTFB、下载各占多少。
- 给一个页面加
preconnect与dns-prefetch,对比首屏。 - 用 Lighthouse 跑一次,列出三个最值得改的项。
② 让 AI 写同一题
把第 2 题(复述整条链路)交给 AI,要求每一步都写出“这一步花的时间由什么决定”。
③ 对比要盯的点
- DNS、TCP、TLS、首字节的先后顺序它写对了吗?
- 强缓存命中发生在哪一步?它有没有把缓存和 DNS 的顺序搞混?
DOMContentLoaded与load的区别它讲清了吗?前者不等图片。
④ 一句话取舍
它给的链路里,哪一步在你这项目上其实被跳过了?
九、自测
- 浏览器缓存的查找顺序是什么?
- 强缓存与协商缓存的区别?
- 关键渲染路径的五个步骤?
- 为什么 CSS 阻塞渲染、JS 阻塞解析?
- TTFB 高应该优化前端吗?
参考答案要点:SW→内存→磁盘→强缓存→协商;是否发请求/304;DOM/CSSOM/Render/Layout/Paint;构建树与脚本执行;先查服务端与网络。
附录:高频追问
问:输入 URL 后第一件事是什么? 解析 URL 并查缓存,不是直接发请求;命中强缓存可能完全不联网。
问:DNS 解析用 TCP 还是 UDP? 通常 UDP,响应过大或区域传送时用 TCP;HTTP/3 下 DNS 也可能走 DoH。
问:HTTP/2 解决了什么? 多路复用、头部压缩、服务端推送(已弱化),解决 HTTP/1.1 的队头阻塞与连接数限制。
问:HTTP/3 为什么更快? 基于 QUIC(UDP),减少握手次数,解决 TCP 层的队头阻塞。
问:defer 和 async 的区别? defer 保证顺序、在 DOMContentLoaded 前执行;async 下载完就执行,顺序不确定。
问:为什么 CSS 要放 head? 尽早构建 CSSOM,避免白屏与无样式闪烁。
问:首屏优化先做什么? 先测量:TTFB、LCP、阻塞资源,再决定是网络、服务端还是渲染问题。
问:预加载会拖慢首屏吗? 会。preload 优先级高,抢关键资源带宽;只给真正关键且发现晚的资源用。
配套题目
★ 越多越该先背;点题目可看完整答案与深入版。
- ★★★★☆ 浏览器的本地存储方式有哪些?怎么选?
- ★★★★☆ 强缓存和协商缓存有什么区别?分别由哪些响应头控制?
- ★★★★☆ 前端错误监控怎么定位到源码?Source Map 应该怎么管理?
- ★★★☆☆ 浏览器多进程架构是怎样的?渲染进程负责什么?
提示:读完可以顺手做几道对应题目,检验是否真的理解