1. 这不是“点开就能懂”的面板,而是前端调试的显微镜
你打开 Chrome 开发者工具,按 F12,切到 Network 面板——这一步,几乎每个写过网页的人都做过。但绝大多数人只停留在“看一眼有没有 404”“找找图片加载慢不慢”的层面。真正用好 Network 面板的人,不到实际前端开发者的三分之一。这不是功能藏得深,而是它背后承载的信息维度远超表面:时间线、协议栈、资源生命周期、缓存决策链、跨域协商细节、TLS 握手耗时、服务端响应策略……它不输出结论,只忠实地记录浏览器与网络世界每一次真实握手的全部脉搏。
我带过的几个刚转行的学员,第一次让我看他们写的管理后台,页面卡顿严重。他们自己反复刷新,盯着 Network 面板里那几十个请求,说“都 200 啊,没报错”。可当我把 Filter 切成largest,再点开一个 800KB 的 JSON 响应,展开 Headers → Response → Content-Encoding,发现是identity(未压缩);再切到 Timing 标签页,看到Content Download占了整个请求耗时的 92%,而Waiting (TTFB)只有 112ms——问题立刻清晰:后端接口没做 Gzip,且前端在没做分页的情况下一次性拉取了全部 5000 条数据。这不是代码 bug,是网络行为误判。
Network 面板真正的价值,从来不是“查错”,而是“读心”:读浏览器的心(它怎么解析、缓存、重试)、读网络的心(DNS 怎么查、TCP 怎么建、TLS 怎么验)、读服务端的心(它想让你缓存多久、是否支持 HTTP/2 推送、响应体结构是否合理)。这篇教程不讲菜单在哪、按钮怎么点,我们直接拆解它最常被忽略的五个核心模块:请求列表的隐藏列逻辑、Timing 图谱的逐毫秒解读、Headers 的协议级语义、Preview/Response 的内容诊断法、以及 Filter 的精准狙击术。你不需要背命令,但必须理解每一列数字背后的物理意义——比如Stalled时间超过 100ms,大概率不是后端慢,而是浏览器在等 DNS 或 TCP 队列;比如ServiceWorker一栏显示from ServiceWorker,却看不到缓存命中时间,说明 SW 没走cache.match()而是直接fetch()了网络。这些判断,决定了你是靠猜,还是靠证据做决策。
适合谁读?三类人:一是刚能写 Vue/React 组件,但一遇到接口超时就只会问“后端是不是挂了”的初级开发者;二是测试同学,需要独立验证接口响应是否符合契约(比如Cache-Control: max-age=3600是否真生效);三是运维或 SRE,需快速定位是 CDN 缓存失效、LB 连接池打满,还是上游服务 TLS 握手异常。只要你每天和 HTTP 请求打交道,这个面板就是你最该磨亮的刀。
2. 请求列表:别只盯着 Name 和 Status,真正关键的是这七列
Network 面板默认只显示 Name、Status、Type、Initiator、Size、Time 六列。但右键表头,你能勾选至少 12 个隐藏列。其中七个,直接决定你能否一眼识别性能瓶颈根源。我建议新手第一步就勾选它们:Protocol、Waterfall、Connection ID、Priority、Cache、ServiceWorker、Encoded Data。下面逐个说透为什么。
2.1 Protocol 列:HTTP/1.1 和 HTTP/2 不只是版本号,是连接模型的彻底重构
当你看到某请求 Protocol 显示h2,别只觉得“哦,用了新协议”。它意味着:
- 多路复用已启用:同一个 TCP 连接上,浏览器可并行发送多个请求帧,不再受 HTTP/1.1 的队头阻塞(Head-of-Line Blocking)限制;
- 服务器推送(Server Push)可能生效:如果后端配置了
Link: </style.css>; rel=preload; as=style,Chrome 会在 HTML 响应返回前,主动推送 CSS 文件(你能在 Initiator 列看到Push); - 头部压缩(HPACK)已工作:HTTP/2 头部二进制编码+静态/动态字典,比 HTTP/1.1 的纯文本头节省 50%+ 传输量。
实操中,如果你发现大量h2请求的Time很低,但Waterfall中Queueing时间波动剧烈(比如忽高忽低),大概率是后端 HTTP/2 实现有缺陷——某些框架(如早期 Spring Boot 2.1)的 Netty HTTP/2 支持在高并发下会因流控策略不当导致帧排队。此时切回 HTTP/1.1 对比测试,若Queueing稳定,问题就定位了。
提示:Protocol 列为空,不代表没用 HTTP/2。检查
chrome://net-internals/#http2,搜索你的域名,看是否有HTTP2_SESSION记录。空值常因浏览器降级(如 TLS 版本不匹配)或服务端明确禁用。
2.2 Waterfall 图谱:不是“看长短”,而是“看形状”和“看间隙”
Waterfall 是 Network 最直观的可视化,但新手常犯两个错误:一是只比总长度,二是忽略横轴单位(毫秒级精度!)。真正要盯的是三个特征:
Stalled 时间 > 50ms 且规律出现:
- 若多个请求 Stalled 时间接近(如都是 120±10ms),极可能是 DNS 查询耗时(Chrome 默认 DNS 缓存 60 秒,但首次访问或缓存过期时需真实查询);
- 若 Stalled 时间随机(如 30ms/210ms/80ms 交替),更可能是 TCP 连接池耗尽,浏览器被迫等待空闲连接(尤其在 HTTP/1.1 下,Chrome 默认同域最多 6 个 TCP 连接)。
Blocking 时间持续存在:
Blocking 是 Chrome 内部状态,表示请求被浏览器策略阻塞(非网络层)。常见原因:- 同域请求数超限(HTTP/1.1 下 6 个连接已满);
- 请求被
document.write()动态插入,触发 parser-blocking; - Service Worker 正在启动,新请求需等待 SW ready。
Gap(间隙)出现在 Waiting (TTFB) 之后:
如果 TTFB 很短(<100ms),但紧接着出现 200ms 空白,然后才开始 Content Download,说明服务端已返回响应头,但响应体生成/传输被阻塞——典型如 Node.js 同步文件读取、PHP 未开启 output buffering、或 Nginxproxy_buffering off导致大文件流式传输卡顿。
2.3 Connection ID 列:识别连接复用与连接泄漏的唯一凭证
HTTP/1.1 下,Connection ID 是 TCP 连接标识符(如123456);HTTP/2 下,它是流 ID(stream ID)。它的核心价值在于:验证连接复用是否生效。
例如,你发起 10 个同域 API 请求:
- 若 Connection ID 全部相同(HTTP/2 下 stream ID 连续递增,如
1,3,5,7...),说明完美复用; - 若 Connection ID 频繁变化(HTTP/1.1 下出现
123456,123457,123458),说明连接未复用,每次都在新建 TCP 连接——查Connection: keep-alive响应头是否缺失,或服务端keepalive_timeout是否设为 0。
更隐蔽的问题:Connection ID 持续增长但不重复(如从1000一路到1050),且页面无刷新,说明存在连接泄漏。常见于 WebSocket 未正确关闭、长轮询未清理、或第三方 SDK 创建的XMLHttpRequest未调用.abort()。
2.4 Priority 列:浏览器的“请求优先级”不是玄学,是可验证的调度策略
Chrome 基于资源类型、HTML 解析位置、<link rel="preload">等自动分配优先级(Highest / High / Medium / Low / Lowest)。但 Priority 列显示的,是浏览器最终执行的调度顺序,而非声明顺序。
关键验证点:
<script>默认Medium,但async或defer脚本会降为Low;<img>在视口内为High,视口外为Low;fetch()发起的请求默认Medium,但可通过priority: 'high'(实验性 API)提升。
实操技巧:在页面加载时,快速切换 Priority 列排序,观察哪些资源被“插队”。如果一个Low优先级的图片请求,其Time却比High的 JS 还早完成,说明网络层(如 CDN 缓存命中)覆盖了浏览器调度——此时 Priority 列告诉你“浏览器想怎么排”,而实际耗时告诉你“现实怎么走”。
2.5 Cache 列:区分“没缓存”和“缓存失效”,是优化的第一步
Cache 列显示disk cache、memory cache、service worker或空白。但新手常混淆:
disk cache:HTTP 缓存(Cache-Control/ETag生效);memory cache:V8 引擎内存缓存(仅限同一会话内,JS/CSS 文件加载后未释放);- 空白:既没走磁盘缓存,也没走内存缓存,一定是网络请求。
重点来了:如何确认缓存是否真的生效?
- 点击请求 → Headers → Response → 查
Cache-Control值(如public, max-age=3600); - 刷新页面,看该请求 Size 是否为
(from disk cache); - 关键验证:修改服务端资源(如 JS 文件加一行注释),但不改文件名,再刷新——若仍显示
from disk cache,说明ETag或Last-Modified未更新,缓存校验失败,浏览器直接复用旧缓存(这是严重 bug)。
注意:强制刷新(Ctrl+F5)会忽略所有缓存,包括 memory cache。日常调试用普通 F5。
2.6 ServiceWorker 列:SW 不是“开关”,是中间拦截器
显示from ServiceWorker仅表示请求被 SW 拦截,但不等于“从缓存返回”。必须点开该请求 → Preview 或 Response 标签页:
- 若内容是预期的缓存数据,且 Headers 中
X-Service-Worker-Cache存在,说明 SW 执行了caches.match(); - 若内容是实时网络响应,且 Headers 有
X-Service-Worker-Fetch,说明 SW 执行了fetch(event.request)并透传。
常见陷阱:SW 注册后,旧缓存未清理。用户首次访问加载 v1 版本,SW 缓存了所有资源;v2 上线后,SW 未在install事件中删除 v1 缓存,导致部分资源仍返回旧版。此时 Network 中from ServiceWorker的请求,内容却是过期的。
2.7 Encoded Data 列:揭开压缩真相的最后屏障
Size 列显示(size / encoded size),如12.4 KB / 3.2 KB。前者是解压后大小,后者是网络传输大小。Encoded Data 列则单独显示编码后字节数(即 3.2 KB)。
为什么重要?
- 若 Encoded Data ≈ Size(如
12.4 KB / 12.4 KB),说明未压缩,需检查服务端 Gzip/Brotli 配置; - 若 Encoded Data 极小(如
12.4 KB / 0.8 KB),但 Time 很长,说明压缩算法耗 CPU(Brotli 级别 11 比 Gzip 级别 6 慢 3 倍),需权衡; - 对于图片,Encoded Data 应接近文件实际大小(如 JPEG 文件本身 150KB),若显示
150 KB / 150 KB,说明未启用水印/CDN 压缩,可优化。
3. Timing 标签页:毫秒级拆解,把一次请求掰成七段来读
点击任意请求 → Timing 标签页,你会看到一张横向时间轴图,下方标注七个阶段:Queuing、Stalled、DNS Lookup、Initial Connection、SSL、Request Sent、Waiting (TTFB)、Content Download。这不是简单的流水线,而是浏览器与网络协议栈的深度协作日志。每个阶段的耗时,都对应一个明确的系统行为。
3.1 Queuing:浏览器的“内部调度室”,藏着资源竞争真相
Queuing 时间 = 请求进入浏览器网络队列,到真正开始处理的时间。它不涉及网络,纯属浏览器内部调度。
典型场景:
- 同域连接数超限:HTTP/1.1 下,Chrome 限制同域最多 6 个 TCP 连接。第 7 个请求必须 Queuing,直到前 6 个中某个完成释放连接。此时 Queuing 时间 ≈ 前 6 个请求中最长的那个
Content Download时间。 - 渲染主线程阻塞:若页面正在执行长任务(如 200ms 的 JS 计算),
fetch()请求会被推迟到主线程空闲,Queuing 时间直线上升。此时需结合 Performance 面板的 Main 线程火焰图交叉验证。 - 低优先级请求让位:
Low优先级的图片请求,可能被High的 JS 请求抢占,Queuing 时间延长。
实操判断:Queuing > 100ms 且伴随多个请求同时出现,基本锁定连接池或主线程问题。解决方案不是“优化后端”,而是前端拆分域名(HTTP/1.1 下)、升级 HTTP/2、或用requestIdleCallback延迟非关键请求。
3.2 Stalled:网络世界的“交通灯”,DNS/TCP/SSL 的第一道关卡
Stalled 是 Queuing 结束后,到 DNS Lookup 开始前的等待时间。它反映的是浏览器准备网络连接的前置耗时。
分解逻辑:
- DNS Lookup 前的 Stalled:浏览器在查 DNS 缓存(本地 hosts、Chrome DNS 缓存、系统 DNS 缓存)。若缓存未命中,Stalled 时间 ≈ DNS 查询延迟(通常 10~100ms,公共 DNS 如 8.8.8.8 可能更久)。
- Initial Connection 前的 Stalled:DNS 已返回 IP,但浏览器需建立 TCP 连接。若目标 IP 的 TCP 连接池已满(同域 6 个),Stalled 时间 = 等待空闲连接的时间。
- SSL 前的 Stalled:TCP 连接已建好,但需进行 TLS 握手。若服务端 TLS 配置复杂(如支持大量旧 cipher suite),Stalled 可能达数百毫秒。
关键技巧:在 Stalled 时间长的请求上右键 → “Copy → Copy all as HAR”,粘贴到 HAR Analyzer ,它会自动标出 DNS/TCP/SSL 各阶段耗时,比肉眼读 Timing 图谱更准。
3.3 DNS Lookup:不只是“查域名”,更是安全与性能的博弈场
DNS Lookup 时间 = 浏览器发起 DNS 查询,到收到 IP 地址响应的时间。理想值 < 30ms。
但要注意:
- HTTPS 请求的 DNS 查询可能被延迟:Chrome 为防 DNS 劫持,对 HTTPS 域名会先查
AAAA(IPv6)记录,若超时再查A(IPv4)。若你的 DNS 服务器 IPv6 响应慢,会导致 DNS Lookup 时间翻倍。 - Preconnect 提前解析:在
<head>中添加<link rel="preconnect" href="https://api.example.com">,可让浏览器在 HTML 解析阶段就发起 DNS 查询,将 DNS Lookup 时间降至 0(Timing 图谱中该阶段消失)。
实测案例:某电商首页,商品图请求 DNS Lookup 平均 85ms。加入preconnect后,首屏图片加载快 120ms。这不是后端优化,是前端对 DNS 协议的理解。
3.4 Initial Connection:TCP 的三次握手,慢在哪里?
Initial Connection = TCP 三次握手完成时间。理论最小值 = 2 × 网络 RTT(往返时延)。若 RTT 是 50ms,Initial Connection 至少 100ms。
异常情况:
- RTT 本身高:用户网络差(如 3G 网络 RTT 300ms),Initial Connection 必然长;
- 服务端 SYN 队列满:Linux
net.ipv4.tcp_max_syn_backlog设置过小,高并发下 SYN 包被丢弃,客户端重试,Initial Connection 暴涨; - NAT 网关问题:企业内网或某些运营商 NAT 设备,对 TCP 连接跟踪超时设置过短(如 30 秒),导致握手包被丢弃。
验证方法:在 Timing 图谱中,Initial Connection 时间 ≈ 2 ×ping该域名的平均时间。若远大于此,问题在服务端或中间设备。
3.5 SSL:TLS 握手的“四次对话”,证书链是最大变量
SSL 阶段 = TLS 握手完成时间。HTTP/1.1 + TLS 1.2 典型耗时 150~300ms;HTTP/2 + TLS 1.3 可压缩至 50~100ms(因 0-RTT 和简化握手)。
影响因素:
- 证书链长度:根证书 → 中间证书 → 域名证书。若服务端未配置中间证书,浏览器需额外发起请求下载,SSL 时间激增;
- 密钥交换算法:RSA 密钥交换比 ECDHE 慢(尤其在低端设备);
- OCSP Stapling:若启用,服务端在握手时直接提供证书吊销状态,避免浏览器额外查询 OCSP 服务器。
实操检查:用openssl s_client -connect api.example.com:443 -servername api.example.com命令,看输出中Verify return code是否为 0(证书有效),以及Certificate chain是否完整。
3.6 Waiting (TTFB):服务端的“思考时间”,也是优化主战场
TTFB(Time to First Byte)= 请求发出,到收到第一个响应字节的时间。它包含:网络传输(请求到服务端)+ 服务端处理(路由、DB 查询、模板渲染)+ 网络传输(响应头到浏览器)。
TTFB < 100ms:优秀;100~300ms:可接受;> 500ms:必须优化。
归因方法:
- 对比同环境其他请求:若所有请求 TTFB 都高,问题在服务端或网络;若仅某接口高,问题在该接口逻辑;
- 服务端日志打点:在请求入口和出口埋点,计算服务端耗时。若服务端耗时 20ms,但 TTFB 400ms,说明网络或 LB 有问题;
- curl 测试:
curl -w "TTFB: %{time_starttransfer}\n" -o /dev/null -s https://api.example.com/data,排除浏览器影响。
经典案例:某后台接口 TTFB 800ms。服务端日志显示处理仅 15ms。用 curl 测试,TTFB 也 780ms。最终发现是 Nginx 配置了resolver 127.0.0.1,但本地 DNS 服务未运行,每次请求都卡在 DNS 解析上。
3.7 Content Download:不是“下载”,是“流式接收”,缓冲区是关键
Content Download = 收到第一个响应字节,到接收完全部响应体的时间。它反映的是网络带宽和响应体大小的乘积。
但注意:
- 流式响应(Streaming):Node.js 的
res.write()或 PHP 的ob_flush(),Content Download 会随数据生成实时增长; - 缓冲区阻塞:若服务端未设置
Transfer-Encoding: chunked,且响应体大,Nginx 可能等全部内容生成后再转发,导致 Content Download 时间集中在最后几秒; - 客户端接收瓶颈:低端手机内存不足,Chrome 可能降低接收缓冲区,Content Download 时间变长。
优化方向:
- 压缩响应体(Gzip/Brotli);
- 分页/分块返回大数据;
- 对图片等二进制资源,启用
Accept-Encoding: br优先协商 Brotli。
4. Headers 与 Preview/Response:从协议语义到内容诊断的完整闭环
Headers 标签页是 HTTP 协议的“解剖台”,Preview/Response 是内容的“CT 扫描仪”。二者结合,才能完成从“请求是否发出”到“响应是否正确”的全链路诊断。
4.1 Request Headers:浏览器的“请求身份证”,每行都有故事
Host: 必须匹配服务端虚拟主机配置,否则 Nginx 返回 400;User-Agent: 服务端据此返回不同格式(如移动端精简版 HTML),也是爬虫识别依据;Accept: 告知服务端“我能接受什么格式”,如application/json, text/plain, */*;Accept-Encoding: 浏览器支持的压缩算法,服务端据此选择 Gzip 或 Brotli;Cookie: 会话凭证,长度超 4KB 可能被截断(HTTP 规范限制);Origin: 跨域请求必带,服务端用它校验Access-Control-Allow-Origin;Referer: 防盗链依据,也是分析流量来源的关键字段。
实操陷阱:
- 开发环境
localhost发起跨域请求,Origin是http://localhost:3000,但后端Access-Control-Allow-Origin配置为*—— 这在简单请求下可行,但若带Authorization头,预检请求(OPTIONS)会失败,因*不允许带凭证。必须精确配置http://localhost:3000。
4.2 Response Headers:服务端的“承诺书”,缓存与安全的核心
Content-Type: 决定浏览器如何解析响应体。text/html渲染为页面,application/json解析为 JS 对象,image/jpeg显示为图片。若后端返回 JSON 却设Content-Type: text/plain,fetch().json()会抛错。Cache-Control: 缓存策略的权威指令。public, max-age=3600表示任何中间代理和浏览器都可缓存 1 小时;no-cache表示每次使用前必须向服务端验证(用ETag或Last-Modified);no-store表示禁止任何缓存。ETag: 资源的指纹哈希值。服务端在If-None-Match头匹配成功时,返回 304 Not Modified,节省带宽。Set-Cookie: 设置 Cookie,含Secure(仅 HTTPS 传输)、HttpOnly(JS 无法读取)、SameSite(防 CSRF)等关键属性。Access-Control-Allow-*: 跨域资源共享头。Access-Control-Allow-Origin必须精确匹配或为*(无凭证时);Access-Control-Allow-Credentials: true时,Origin不能为*。
关键验证:修改前端代码,让请求带上Cache-Control: no-cache,观察 Response Headers 中ETag是否变化。若不变,说明服务端未根据请求参数生成新 ETag,缓存校验失效。
4.3 Preview 标签页:JSON/XML/HTML 的“所见即所得”预览
Preview 自动格式化常见响应体:
- JSON:折叠/展开对象,高亮语法,点击 key 可复制值;
- HTML:渲染为可交互页面,右键可审查元素;
- XML:树状结构展示,支持搜索节点。
但 Preview 有局限:
- 不显示原始字节:若响应体含不可见字符(如 UTF-8 BOM
\uFEFF),Preview 会过滤,但实际传输存在,可能导致 JS 解析失败; - 不支持二进制:图片、PDF 显示为乱码,需切到 Response 标签页或右键“Open in new tab”;
- 截断大内容:Preview 默认只显示前 5MB,超长 JSON 会被截断,此时必须看 Response。
实操技巧:对 API 响应,右键 Preview 中的 JSON → “Store as global variable”,Chrome 控制台会生成temp1变量,可直接console.log(temp1.data)调试,比手动复制粘贴快十倍。
4.4 Response 标签页:原始字节的“显微镜”,调试的终极战场
Response 显示未经处理的原始响应体,是验证编码、校验签名、排查乱码的唯一途径。
核心操作:
- 查看编码:右上角显示当前编码(如
UTF-8)。若中文显示为 ``,点击编码名,尝试GBK、ISO-8859-1; - 搜索内容:Ctrl+F 直接在原始响应中搜索关键词(如
"error":true),比在 Preview 中更快; - 复制原始内容:右键 → “Copy response”,获取完整字节流,可用于 Postman 复现或服务端日志比对;
- 二进制分析:对图片响应,右键 → “Open in new tab” 查看是否损坏;对 PDF,检查是否能正常打开。
经典问题:某接口返回 JSON,Preview 显示正常,但前端JSON.parse()报错。切到 Response,发现开头有不可见字符U+FEFF(BOM),复制到编辑器显示为{"data": [...]}。解决方案:后端响应前移除 BOM,或前端response.text().then(t => t.replace(/^\uFEFF/, ''))。
5. Filter 与高级技巧:从“大海捞针”到“精准狙击”
Network 面板默认记录所有请求(HTML、JS、CSS、图片、XHR、Fetch、WebSocket、Manifest…),页面复杂时轻松破千条。Filter 不是“搜索框”,而是正则驱动的“请求过滤引擎”。
5.1 基础 Filter:用好四个符号,效率提升 5 倍
filter by name: 输入login,匹配 URL、文件名、路径含 login 的请求;filter by status:status-code:404,status-code:200-299(2xx 范围);filter by type:type:fetch(现代 fetch API),type:xhr(XMLHttpRequest),type:script;filter by domain:domain:api.example.com,精准定位某服务。
组合技:login status-code:400 type:fetch—— 查找所有向 login 相关接口发起的、返回 400 的 fetch 请求。
注意:Filter 是“AND”逻辑,不是“OR”。要查 400 或 500,需用正则
status-code:400|500。
5.2 正则 Filter:用^$锁定请求,避免误伤
默认 Filter 是模糊匹配。要精确控制,必须用正则:
^/api/v2/users$:只匹配/api/v2/users这个完整路径,不匹配/api/v2/users/123;\.js$:匹配所有以.js结尾的请求(JS 文件),\.css$同理;^https?://.*\.googleapis\.com/:匹配所有 Google APIs 域名的请求。
实操案例:某页面引入了 20 个第三方 SDK,Network 里全是cdn.xxx.com/sdk.js。用cdn\.xxx\.comFilter 后,仍混杂着cdn.xxx.com/analytics.js和cdn.xxx.com/chat.js。此时用cdn\.xxx\.com/analytics\.js$精准定位分析脚本,排除干扰。
5.3 高级技巧:录制、保存、对比,让调试可追溯
- 录制控制:左上角红色圆点是录制开关。关闭后,新请求不记录,但已存在的请求仍可分析。适合在关键操作(如提交表单)前开启,避免无关请求刷屏。
- 保存为 HAR:右键任意请求 → “Save all as HAR with content”,生成 .har 文件。这是标准网络日志格式,可用 HAR Viewer 在线分析,或导入 Wireshark 深度解码。
- 对比两次请求:录制 A 操作的 HAR,再录制 B 操作的 HAR,用 HAR Diff 工具对比,自动标出响应时间、Header、Size 差异。
- 禁用缓存调试:勾选左上角 “Disable cache”,强制所有请求走网络(绕过 disk/memory cache),但注意:这也会禁用 Service Worker 的
fetch事件,需结合bypass for network使用。
5.4 常见问题速查表:从现象到根因的 10 分钟定位
| 现象 | 可能根因 | 快速验证步骤 |
|---|---|---|
| 所有请求 Stalled > 200ms | DNS 服务器响应慢或不可达 | nslookup your-domain.com;检查chrome://net-internals/#dns |
| 某接口 TTFB 稳定 800ms,但服务端日志显示 20ms | Nginx/LB 到服务端网络延迟高,或服务端数据库连接池满 | curl -w "TTFB: %{time_starttransfer}\n" -o /dev/null -s http://backend-ip:port/api(绕过 LB) |
Preview 显示 JSON,但 JS 报Unexpected token | 响应体含 BOM 或非法 Unicode 字符 | 切到 Response,Ctrl+A 复制,粘贴到 JSONLint 验证 |
from ServiceWorker但内容是旧版 | SW 缓存未更新,或caches.match()逻辑错误 | 在 Application → Cache Storage 中,手动删除旧缓存,刷新测试 |
图片请求 Size 显示(from disk cache),但实际是新图 | 服务端Cache-Control未随文件变更,或ETag未更新 | 修改图片文件,检查响应 Header 中ETag是否变化 |
WebSocket 连接频繁断开,Network 显示failed | 服务端心跳超时,或反向代理(Nginx)proxy_read_timeout过短 | 检查 Nginx 配置proxy_read_timeout 60;,服务端心跳间隔 < 30s |
type:fetch请求在 Network 中不显示 | Fetch 请求被 Service Worker 拦截且未调用event.respondWith() | 在 Application → Service Workers 中,勾选 “Update on reload”,刷新看是否出现 |
font类型请求 Size 为(from memory cache),但字体未渲染 | @font-face的srcURL 与实际请求 URL 不一致(如协议不同) | 检查 CSS 中url('https://...')是否与 Network 中请求的http://...协议匹配 |
status-code:0的请求大量出现 | 请求被浏览器取消(如页面跳转、AJAX 被 abort)或 CORS 预检失败 | 查看 Initiator 列,若为other,检查是否页面卸载时发起请求 |
Content Download时间远超TTFB,但网络带宽充足 | 服务端未启用Transfer-Encoding: chunked,或响应体生成慢 | 用curl -N(流式输出)测试,观察是否数据延迟到达 |
5.5 我踩过的坑:三个血泪教训,省你三天排查时间
“缓存没问题”是最危险的假设:
曾有个项目,用户反馈“修改了头像,刷新后还是旧的”。Network 显示from disk cache,Headers 里Cache-Control: public, max-age=31536000。我以为是强缓存问题,清缓存、换浏览器都不行。最后发现是服务端 CDN 配置了Cache-Control: immutable,且ETag基于文件名而非内容生成——用户上传新头像,文件名不变(avatar.jpg),ETag不变,CDN 直接返回旧缓存。解决方案:后端返回ETag: "<hash-of-content>",或前端上传时加时间戳参数?t=1712345678。Preload不等于“提前加载完成”:
为加速首屏,我在<head>加了<link rel="preload" href="/main.js" as="script">。Network 中main.js确实提前发起请求,但Time比