前端网络调试完全指南
🎯 DevTools + Charles + Wireshark 三大神器,线上问题排查实战
1. Chrome DevTools Network
1.1 面板功能总览
┌─────────────────────────────────────────────────────────────────────────┐
│ Network 面板功能 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ 工具栏 ─────────────────────────────────────────────────────────┐ │
│ │ 🔴 录制 🚫 清除 📥 导出HAR 🔍 过滤 ☐ Preserve log │ │
│ │ ☐ Disable cache 🌐 Throttling ☐ No throttling │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ 过滤器 ─────────────────────────────────────────────────────────┐ │
│ │ All | Fetch/XHR | JS | CSS | Img | Media | Font | Doc | WS | ... │ │
│ │ Filter: domain:api.example.com status-code:500 larger-than:100k │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ 请求列表 ───────────────────────────────────────────────────────┐ │
│ │ Name Status Type Initiator Size Time Waterfall │ │
│ │ ─────────────────────────────────────────────────────────────── │ │
│ │ api/users 200 fetch main.js:42 1.2kB 120ms ▓▓▓░░░░░ │ │
│ │ main.js 200 script index.html 45kB 80ms ▓▓░░░░░░ │ │
│ │ style.css (cache) style index.html 12kB 10ms ▓░░░░░░░ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ 请求详情 (点击单个请求) ────────────────────────────────────────┐ │
│ │ Headers | Preview | Response | Initiator | Timing | Cookies │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘1.2 高级过滤语法
bash
# 按域名
domain:api.example.com
# 按状态码
status-code:500
status-code:4xx
# 按大小
larger-than:100k
larger-than:1M
# 按类型
is:running # 进行中的请求
method:POST # POST 请求
mime-type:application/json
# 排除
-status-code:200 # 非 200 的请求
-domain:cdn.example.com # 排除 CDN 请求
# 组合
domain:api.example.com status-code:5xx1.3 Timing 详解
┌─────────────────────────────────────────────────────────────────────────┐
│ 请求 Timing 详解 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 时间线: │
│ ──────────────────────────────────────────────────────────────────── │
│ │ Queueing │ Stalled │ DNS │ TCP │ TLS │ Request │ Waiting │ Content│ │
│ ──────────────────────────────────────────────────────────────────── │
│ │
│ 各阶段说明: │
│ ─────────── │
│ Queueing - 排队等待 (浏览器连接数限制) │
│ Stalled - 停滞 (等待代理/缓存检查) │
│ DNS Lookup - DNS 解析 │
│ Initial Connection - TCP 三次握手 │
│ SSL/TLS - TLS 握手 │
│ Request Sent - 发送请求 │
│ Waiting (TTFB) - 等待首字节 (服务器处理时间) │
│ Content Download - 下载响应体 │
│ │
│ 常见问题诊断: │
│ ────────────── │
│ TTFB 过长 → 服务器处理慢 / 网络延迟 │
│ Queueing 长 → 请求太多,被浏览器排队 │
│ DNS 慢 → DNS 解析问题,考虑 dns-prefetch │
│ SSL 慢 → 考虑 TLS 1.3 / 会话复用 │
│ │
└─────────────────────────────────────────────────────────────────────────┘1.4 Waterfall 瀑布图分析
┌─────────────────────────────────────────────────────────────────────────┐
│ Waterfall 颜色含义 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 颜色说明: │
│ ───────── │
│ ░░░ 白色/浅色 - Queueing / Stalled │
│ ▒▒▒ 浅绿色 - DNS Lookup │
│ ▓▓▓ 橙色 - Initial Connection (TCP) │
│ ███ 紫色 - SSL/TLS │
│ ███ 绿色 - Request Sent + Waiting (TTFB) │
│ ███ 蓝色 - Content Download │
│ │
│ 优化目标: │
│ ───────── │
│ 理想: 蓝色占比大 (下载快) │
│ 问题: 绿色占比大 (TTFB 慢,服务器问题) │
│ 问题: 白色占比大 (请求排队,减少请求数) │
│ │
└─────────────────────────────────────────────────────────────────────────┘1.5 实用技巧
javascript
// 1. Copy as fetch - 复制请求为 fetch 代码
// 右键请求 → Copy → Copy as fetch
// 2. Replay XHR - 重放请求
// 右键请求 → Replay XHR
// 3. Block request URL - 阻止特定请求
// 右键请求 → Block request URL
// 测试降级场景
// 4. Override response - 覆盖响应
// Sources → Overrides → Select folder
// 可以修改响应内容调试
// 5. 命令行快速过滤
// Ctrl + Shift + P → 输入 "Network"2. Charles 抓包
2.1 基础配置
┌─────────────────────────────────────────────────────────────────────────┐
│ Charles 配置步骤 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 安装 Charles │
│ https://www.charlesproxy.com/ │
│ │
│ 2. 配置 HTTPS 抓包 │
│ Proxy → SSL Proxying Settings → Add │
│ Host: * (或具体域名) │
│ Port: 443 │
│ │
│ 3. 安装根证书 │
│ Help → SSL Proxying → Install Charles Root Certificate │
│ macOS: 钥匙串 → 系统 → 双击证书 → 信任 → 始终信任 │
│ │
│ 4. 配置浏览器代理 │
│ Charles 默认端口: 8888 │
│ 或安装 SwitchyOmega 扩展 │
│ │
│ 5. 移动端配置 │
│ Help → Local IP Address → 记录 IP │
│ 手机 WiFi → 代理 → 手动 → IP:8888 │
│ Safari 访问 chls.pro/ssl 安装证书 │
│ │
└─────────────────────────────────────────────────────────────────────────┘2.2 常用功能
┌─────────────────────────────────────────────────────────────────────────┐
│ Charles 常用功能 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Throttle (限速) │
│ ───────────── │
│ Proxy → Throttle Settings │
│ 模拟 3G/慢速网络,测试加载性能 │
│ │
│ 2. Map Remote (远程映射) │
│ ─────────────────── │
│ Tools → Map Remote │
│ 将线上请求映射到本地服务器 │
│ 例: https://api.prod.com → http://localhost:3000 │
│ │
│ 3. Map Local (本地映射) │
│ ──────────────────── │
│ Tools → Map Local │
│ 将请求映射到本地文件 │
│ 例: /api/users → /Users/me/mock/users.json │
│ │
│ 4. Rewrite (重写) │
│ ────────────── │
│ Tools → Rewrite │
│ 修改请求/响应的 Header、Body、URL │
│ │
│ 5. Breakpoints (断点) │
│ ───────────────── │
│ Proxy → Breakpoint Settings │
│ 拦截请求/响应,手动修改后再发送 │
│ │
│ 6. Repeat (重复请求) │
│ ───────────────── │
│ 右键请求 → Repeat / Repeat Advanced │
│ 压测或重现问题 │
│ │
└─────────────────────────────────────────────────────────────────────────┘2.3 Mock 数据
json
// Map Local 示例
// Charles → Tools → Map Local → Add
// Map From:
// Host: api.example.com
// Path: /api/users
// Map To:
// Local Path: /Users/me/mock/users.json
// users.json 内容:
{
"code": 0,
"data": {
"list": [
{ "id": 1, "name": "Mock User 1" },
{ "id": 2, "name": "Mock User 2" }
],
"total": 2
},
"message": "success"
}2.4 Rewrite 规则
// 修改响应 Header
Location: Response
Type: Add Header
Match: *
Replace:
Name: Access-Control-Allow-Origin
Value: *
// 修改响应 Body
Location: Response
Type: Body
Match: "error": true
Replace: "error": false
// 修改请求 URL
Location: URL
Type: Path
Match: /api/v1/
Replace: /api/v2/3. 移动端调试
3.1 iOS Safari 调试
┌─────────────────────────────────────────────────────────────────────────┐
│ iOS Safari 远程调试 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 准备工作: │
│ ───────── │
│ 1. iPhone: 设置 → Safari → 高级 → 网页检查器 → 开启 │
│ 2. Mac: Safari → 偏好设置 → 高级 → 显示开发菜单 │
│ │
│ 调试步骤: │
│ ───────── │
│ 1. USB 连接 iPhone 到 Mac │
│ 2. iPhone 用 Safari 打开目标网页 │
│ 3. Mac Safari → 开发 → [设备名] → [网页] │
│ 4. 打开 Web Inspector,与桌面版功能相同 │
│ │
│ 注意: │
│ ────── │
│ - 需要 macOS + Safari │
│ - 适用于调试 Safari、WebView │
│ - 不适用于第三方浏览器 (Chrome iOS 等) │
│ │
└─────────────────────────────────────────────────────────────────────────┘3.2 Android Chrome 调试
┌─────────────────────────────────────────────────────────────────────────┐
│ Android Chrome 远程调试 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 准备工作: │
│ ───────── │
│ 1. Android: 设置 → 开发者选项 → USB 调试 → 开启 │
│ 2. Chrome 版本 ≥ 32 │
│ │
│ 调试步骤: │
│ ───────── │
│ 1. USB 连接 Android 到电脑 │
│ 2. Android 用 Chrome 打开目标网页 │
│ 3. 电脑 Chrome 访问 chrome://inspect │
│ 4. 找到设备和网页,点击 "inspect" │
│ 5. 打开 DevTools,可以看到手机屏幕镜像 │
│ │
│ WebView 调试: │
│ ───────────── │
│ APP 需要在代码中开启: │
│ WebView.setWebContentsDebuggingEnabled(true); │
│ │
└─────────────────────────────────────────────────────────────────────────┘3.3 vConsole / Eruda
html
<!-- vConsole - 移动端调试面板 -->
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
// 仅在开发/测试环境启用
if (location.hostname !== 'prod.example.com') {
new VConsole();
}
</script>
<!-- Eruda - 功能更丰富 -->
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>
eruda.init();
</script>javascript
// 条件加载 (推荐)
function loadDebugTool() {
const script = document.createElement('script');
script.src = 'https://unpkg.com/vconsole@latest/dist/vconsole.min.js';
script.onload = () => new VConsole();
document.head.appendChild(script);
}
// URL 参数触发
if (location.search.includes('debug=1')) {
loadDebugTool();
}
// 手势触发 (5 次点击)
let clickCount = 0;
document.addEventListener('click', () => {
clickCount++;
if (clickCount >= 5) {
loadDebugTool();
clickCount = 0;
}
setTimeout(() => clickCount = 0, 3000);
});4. 线上问题排查
4.1 常见问题清单
┌─────────────────────────────────────────────────────────────────────────┐
│ 线上网络问题排查清单 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ □ 请求不发出 │
│ ├── CORS 预检失败 (看 Console 和 Network) │
│ ├── 代码逻辑问题 (断点调试) │
│ ├── URL 拼接错误 │
│ └── 请求被拦截器阻止 │
│ │
│ □ 请求超时 │
│ ├── 网络不稳定 (测试其他请求) │
│ ├── 服务端处理慢 (看 TTFB) │
│ ├── DNS 解析慢 (看 Timing) │
│ └── 防火墙/代理拦截 │
│ │
│ □ 返回 4xx │
│ ├── 401: Token 过期/无效 │
│ ├── 403: 权限不足 │
│ ├── 404: URL 路径错误 │
│ └── 422: 参数格式错误 │
│ │
│ □ 返回 5xx │
│ ├── 500: 服务器内部错误 (联系后端) │
│ ├── 502: 网关错误 (Nginx/负载均衡问题) │
│ ├── 503: 服务不可用 (服务宕机) │
│ └── 504: 网关超时 (后端处理超时) │
│ │
│ □ 数据异常 │
│ ├── 检查请求参数是否正确 │
│ ├── 检查响应数据格式 │
│ ├── 检查是否有缓存问题 │
│ └── 检查是否有编码问题 (UTF-8) │
│ │
└─────────────────────────────────────────────────────────────────────────┘4.2 CORS 问题排查
┌─────────────────────────────────────────────────────────────────────────┐
│ CORS 问题排查 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 症状: │
│ ────── │
│ Console: "Access to fetch at 'xxx' from origin 'xxx' has been │
│ blocked by CORS policy" │
│ │
│ 排查步骤: │
│ ───────── │
│ 1. Network 面板找到请求 │
│ 2. 检查是否有 OPTIONS 预检请求 │
│ 3. 检查预检请求的响应头: │
│ - Access-Control-Allow-Origin │
│ - Access-Control-Allow-Methods │
│ - Access-Control-Allow-Headers │
│ │
│ 常见原因: │
│ ───────── │
│ 1. 服务端未配置 CORS 头 │
│ 2. Allow-Origin 是 * 但使用了 credentials │
│ 3. 自定义 Header 未在 Allow-Headers 中 │
│ 4. 请求方法未在 Allow-Methods 中 │
│ │
│ 临时绕过 (仅开发): │
│ ───────────────── │
│ 1. 使用开发代理 (vite proxy) │
│ 2. 浏览器禁用安全检查 (不推荐) │
│ Chrome: --disable-web-security │
│ │
└─────────────────────────────────────────────────────────────────────────┘4.3 调试代码注入
javascript
// 线上注入调试代码
// 在 Console 中执行
// 1. 拦截所有 fetch 请求
const originalFetch = window.fetch;
window.fetch = async (...args) => {
console.log('[Fetch]', args[0], args[1]);
const start = Date.now();
try {
const response = await originalFetch(...args);
console.log('[Fetch Response]', args[0], response.status, `${Date.now() - start}ms`);
return response;
} catch (error) {
console.error('[Fetch Error]', args[0], error);
throw error;
}
};
// 2. 拦截所有 XHR 请求
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._method = method;
this._url = url;
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function(body) {
console.log('[XHR]', this._method, this._url, body);
this.addEventListener('load', () => {
console.log('[XHR Response]', this._url, this.status);
});
return originalSend.apply(this, arguments);
};
// 3. 监控性能
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'resource') {
console.log('[Resource]', entry.name, entry.duration.toFixed(2) + 'ms');
}
}
});
observer.observe({ entryTypes: ['resource'] });5. 性能分析
5.1 Performance API
javascript
// 获取所有资源加载时间
const resources = performance.getEntriesByType('resource');
resources.forEach((entry) => {
console.log({
name: entry.name,
type: entry.initiatorType,
duration: entry.duration.toFixed(2) + 'ms',
size: entry.transferSize,
// 各阶段时间
dns: (entry.domainLookupEnd - entry.domainLookupStart).toFixed(2),
tcp: (entry.connectEnd - entry.connectStart).toFixed(2),
ttfb: (entry.responseStart - entry.requestStart).toFixed(2),
download: (entry.responseEnd - entry.responseStart).toFixed(2),
});
});
// 获取导航时间
const nav = performance.getEntriesByType('navigation')[0];
console.log({
// 关键指标
domContentLoaded: nav.domContentLoadedEventEnd.toFixed(2) + 'ms',
load: nav.loadEventEnd.toFixed(2) + 'ms',
// 各阶段
redirect: (nav.redirectEnd - nav.redirectStart).toFixed(2),
dns: (nav.domainLookupEnd - nav.domainLookupStart).toFixed(2),
tcp: (nav.connectEnd - nav.connectStart).toFixed(2),
request: (nav.responseStart - nav.requestStart).toFixed(2),
response: (nav.responseEnd - nav.responseStart).toFixed(2),
domParsing: (nav.domInteractive - nav.responseEnd).toFixed(2),
});
// 监控慢请求
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.duration > 3000) { // 超过 3 秒
console.warn('[Slow Resource]', entry.name, entry.duration);
// 上报监控系统
}
});
});
observer.observe({ entryTypes: ['resource'] });5.2 Lighthouse
bash
# CLI 运行
npx lighthouse https://example.com --view
# Chrome DevTools
# → Lighthouse 面板 → Generate report
# 关注指标
# - Performance: 性能评分
# - FCP: First Contentful Paint
# - LCP: Largest Contentful Paint
# - TBT: Total Blocking Time
# - CLS: Cumulative Layout Shift6. 面试高频问题
Q1: 如何分析请求慢的原因?
- 打开 Network 面板,找到慢请求
- 查看 Timing 选项卡
- 分析各阶段耗时:
- DNS 慢 → dns-prefetch
- TCP/TLS 慢 → HTTP/2、预连接
- TTFB 慢 → 服务端优化
- Download 慢 → 压缩、CDN
Q2: 如何调试线上问题?
- 复现问题,记录环境信息
- 打开 DevTools Network,开启 Preserve log
- 分析失败请求的状态码和响应
- 检查 Console 报错
- 使用 Charles 抓包详细分析
- 必要时注入调试代码
Q3: CORS 预检请求什么时候触发?
触发条件(满足任一):
- HTTP 方法不是 GET/HEAD/POST
- Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data、text/plain
- 包含自定义 Header
- 使用了 ReadableStream
Q4: 如何调试移动端页面?
- iOS: Safari 远程调试(需 Mac)
- Android: chrome://inspect
- 通用: vConsole / Eruda
- 抓包: Charles + 手机代理
Q5: 如何分析首屏加载性能?
- Performance API 获取关键时间点
- Lighthouse 生成性能报告
- Network 瀑布图分析阻塞资源
- 关注 LCP、FCP、TTFB 指标