← 返回面试目录

前端 Debug 实战指南

真实场景 · 排查流程 · DevTools技巧 · 思维模型 · 手把手排查

白屏排查 DevTools 网络问题 CSS Bug 内存泄漏 构建报错

白屏排查流程(最重要!)

白屏是前端最常见也最让人慌的问题。按照下面的流程走,99% 的白屏都能在 10 分钟内定位原因。

1页面白屏!
2打开 DevTools
F12 / Cmd+Option+I
3Console 有红色报错吗?
有 → 看报错定位
4Network 有失败请求吗?
红色请求 → 看状态码
5JS 加载了吗?
Network 面板找 .js 文件
6检查路由配置
history 模式?nginx?

白屏就像手机App开门了但客人看到空荡荡的柜台 —— 是吧台没搭好(JS没加载)?是配方丢了(路由配错)?还是供货商没来(接口挂了)?挨个检查!

6 种常见白屏原因 + 解决方案

#原因现象排查 / 解决
1 JS 报错(入口 crash) Console 红色报错,页面空白 看 Console 红色报错,定位到具体文件和行号。常见:变量未定义、import 路径错误
2 路由配置错误 刷新 404,首页能进子页面不行 history 模式需要 nginx 配置 try_files;或改用 hash 模式
3 CSP / 白名单限制 Console 提示 Refused to load 检查 meta 标签 CSP 策略和响应头 Content-Security-Policy
4 打包路径错误 Network 里 JS/CSS 404 检查 vite 的 base 或 webpack 的 publicPath 配置
5 接口报错导致渲染失败 组件 render 报错,整棵树崩了 加 ErrorBoundary(React)/ onErrorCaptured(Vue);接口调用加 try-catch
6 CSS 加载失败 页面内容在但完全无样式 检查 Network 面板 CSS 文件是否 404;检查 extract 插件配置

路由 history 模式 nginx 配置

# nginx.conf - SPA 路由 history 模式必须配这个
server {
  listen 80;
  server_name example.com;
  root /usr/share/nginx/html;

  location / {
    # 关键:所有路由都返回 index.html,交给前端路由处理
    try_files $uri $uri/ /index.html;
  }
}

ErrorBoundary 示例(React)

// React ErrorBoundary - 防止子组件报错导致整棵树白屏
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <h2>页面出错了,请刷新重试</h2>;
    }
    return this.props.children;
  }
}

DevTools 调试技巧

DevTools 就是手机App的后厨监控屏 —— 哪台机器卡了(长任务)、哪个原料没了(请求失败)、哪杯手机App做错了(数据不对),一目了然!

Console 面板 —— 你的第一个战场

// 基础输出
console.log('普通日志');
console.warn('警告信息'); // 黄色
console.error('错误信息'); // 红色,带堆栈

// 格式化输出(开发必备)
console.table(users); // 数组/对象变表格,超好用!
console.group('用户信息');
console.log('姓名', user.name);
console.log('年龄', user.age);
console.groupEnd();

// 计时
console.time('接口耗时');
await fetch('/api/users');
console.timeEnd('接口耗时'); // 输出: 接口耗时: 234ms

// 清除
console.clear(); // 清屏

Sources 面板 —— 断点调试

断点类型用法适用场景
普通断点 点行号左侧设置 代码走到这里时暂停,逐行调试
条件断点 右键行号 → "Add conditional breakpoint" 只在满足条件时断,如 userId === 123
XHR 断点 Sources → XHR/fetch Breakpoints → 填 URL 某个接口请求时自动断住
DOM 断点 Elements 面板右键元素 → Break on DOM 被修改时断住,排查谁改了节点
事件监听断点 Sources → Event Listener Breakpoints click/scroll 等事件触发时断住

快捷键:F8 继续执行  |  F10 单步跳过  |  F11 单步进入  |  Shift+F11 跳出

Network 面板 —— 看请求瀑布流

红色请求 = 失败(看状态码:404 找不到、500 服务器错、CORS 被 block)

瀑布流 = 看加载顺序,哪个请求卡住了后面的(瓶颈在哪)

Filter = 输入关键词快速过滤(如输入 "api" 只看接口)

右键 → Copy as cURL = 把请求复制成命令行,直接给后端同事复现

Disable cache = 勾上后每次都是全新请求,排除缓存干扰

Elements 面板 —— CSS 实时调试

强制状态:点击 :hov 按钮,勾选 :hover / :focus / :active,不用鼠标悬停也能看样式

修改样式:直接在 Styles 面板改数值,实时看效果(调试布局神器)

Box Model:看 margin / border / padding / content 的实际尺寸

Computed:看最终生效的样式值(优先级最高的那个)

Performance 面板 —— 性能分析

点击录制
操作页面
停止录制
看火焰图

红色三角 = 长任务(>50ms),需要优化

Main 线程 = 看 JS 执行耗时

Summary 饼图 = Loading / Scripting / Rendering / Painting 各占多少

网络与接口问题

CORS 跨域排查流程

Console 报错:
Access-Control-Allow-Origin
是简单请求还是预检?
GET/POST 无自定义头 = 简单请求
简单请求:检查 Response
有没有 Access-Control-Allow-Origin
|
预检请求:OPTIONS 先发
检查服务端是否处理了 OPTIONS

跨域解决方案对比

方案原理优点缺点
开发代理 vite/webpack devServer 代理到后端 零配置后端,开发环境最常用 只适合开发环境
nginx 反向代理 同域转发,浏览器不知道后端在哪 生产环境标准方案 需要运维配 nginx
后端 CORS 头 服务端返回 Allow-Origin 响应头 灵活,支持多域名 需要后端改代码
JSONP 利用 script 标签不受同源限制 兼容老浏览器 只支持 GET,有安全风险,已过时

开发代理配置(vite / webpack)

// vite.config.js - 开发环境代理
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        // rewrite: path => path.replace(/^\/api/, '')
      }
    }
  }
});

nginx 代理配置 & CORS 头配置

# nginx 反向代理方案(生产环境推荐)
server {
  listen 80;
  server_name example.com;

  # 静态资源
  location / {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html;
  }

  # API 代理到后端
  location /api/ {
    proxy_pass http://backend:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}
# 后端 CORS 响应头配置(以 nginx 为例)
location /api/ {
  add_header 'Access-Control-Allow-Origin' 'https://example.com';
  add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
  add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
  add_header 'Access-Control-Max-Age' 86400;

  # 预检请求直接返回 204
  if ($request_method = 'OPTIONS') {
    return 204;
  }
}

接口超时排查

前端超时:检查 axios/fetch 的 timeout 配置,默认可能只有 10s

后端慢:Network 面板看 Waiting (TTFB) 耗时,如果这个很长说明后端处理慢

网络慢:Content Download 耗时长,可能是返回数据太大或网络差

DNS 解析慢:看 Timing 面板 Stalled 阶段,首次请求慢可能是 DNS 解析

请求 / 响应数据不对

数据不对
看 Request Payload
前端传的对吗?
看 Response
后端返回的对吗?
定位到哪一方
谁错找谁

• 请求参数不对 → 检查前端序列化(JSON.stringify? FormData?)

• 响应数据不对 → 把请求 Copy as cURL 给后端同事

• 都对但页面显示不对 → 检查前端数据处理逻辑

CSS 布局 Bug 排查

CSS 排版就像手机App摆桌椅 —— 椅子不见了(display:none)、桌子挤到门外了(overflow)、装修图纸和实际不一样(盒模型)。用 Elements 面板一看就知道!

元素消失了?

display: none → 完全消失,不占空间。检查是谁设置的(JS?媒体查询?)

visibility: hidden → 不可见但占空间。检查是否有动画/过渡残留

opacity: 0 → 透明但占空间且可点击。常见于动画未结束

overflow: hidden → 内容被裁切了。检查父容器的 overflow 和高度

z-index 太低 → 被其他元素盖住了。在 Elements 面板看层级

height: 0 → 高度为 0。检查子元素是否 float 没清(BFC 问题)

元素溢出了?

width/height 超出父容器 → 检查是否设了固定宽度,改成 max-width

overflow 值不对 → 需要滚动加 overflow: auto,需要隐藏加 overflow: hidden

box-sizing → 检查是不是 content-box 导致 padding 撑大了。全局加 box-sizing: border-box

长单词/URL → 加 word-break: break-alloverflow-wrap: break-word

布局偏移了?

margin 塌陷 → 父子元素的上外边距合并了。解决方案:给父元素加 overflow: hidden 或 padding-top: 1px

BFC → 触发 BFC 隔离布局:overflow: hidden / display: flow-root / position: absolute

position 用错了 → relative 是相对自身偏移,absolute 是相对最近的定位祖先,fixed 是相对视口

flex 布局 → 检查 justify-content / align-items / flex-shrink / flex-grow 是否符合预期

不居中?居中方案速查

/* 方案1:Flex 居中(最推荐) */
.parent {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
}

/* 方案2:Grid 居中(最简洁) */
.parent {
  display: grid;
  place-items: center; /* 一行搞定水平+垂直 */
}

/* 方案3:绝对定位 + transform */
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

移动端常见 CSS 问题

1px 边框问题

移动端 1px 边框在高 DPI 屏幕上看起来很粗。用 transform 缩放:

/* 1px 边框解决方案 */
.border-1px::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: #ccc;
  transform: scaleY(0.5);
  transform-origin: 0 0;
}

安全区适配(刘海屏 / 底部横条)

/* 安全区适配 - env() 函数 */
.header {
  padding-top: env(safe-area-inset-top);
}
.bottom-bar {
  padding-bottom: env(safe-area-inset-bottom);
}

/* 配合 viewport meta 使用 */
<meta name="viewport" content="width=device-width, initial-scale=1.0,
  viewport-fit=cover">

内存泄漏排查

什么是内存泄漏?为什么 SPA 容易泄漏?

内存泄漏 = 用完的东西没扔掉,越积越多,最后页面卡死甚至崩溃。

SPA(单页应用)特别容易泄漏,因为:

• 页面不会整页刷新(没有天然的重置)

• 组件反复挂载/卸载,每次都可能留下残留

• 路由切换时,旧页面的事件监听器、定时器可能没清理

内存泄漏就像手机App的垃圾桶忘了倒 —— 用的杯子、吸管一直往里扔,但从来没清过,最后垃圾桶满了,店都转不开了。SPA 不刷新页面 = 垃圾桶永远不倒!

Chrome DevTools Memory 面板使用步骤

1拍快照(Heap Snapshot)
2操作页面
反复进出某个路由 10 次
3再拍一次快照
4对比两次快照
看 Delta 列,找出持续增长的对象

判断标准:操作前后,如果 Detached DOM 节点在增长,或者某个对象数量只增不减,大概率就是泄漏了。

5 种常见泄漏场景

#泄漏场景原因修复
1 定时器未清除 setInterval/setTimeout 组件卸载后还在跑 在 beforeUnmount/useEffect cleanup 中 clearInterval
2 事件监听未移除 addEventListener 后没有 removeEventListener 卸载时移除;或用 AbortController
3 闭包引用 闭包持有外部变量,导致无法 GC 及时置空引用,避免不必要的闭包
4 DOM 引用 JS 变量引用了已移除的 DOM 节点 DOM 移除后把 JS 引用置为 null
5 全局变量/缓存 往 window 上挂数据,页面不刷新就不会释放 避免全局变量;使用弱引用 WeakMap/WeakSet

Vue 清理定时器

// Vue 3 - onUnmounted 中清理定时器
import { onUnmounted } from 'vue'

export default {
  setup() {
    let timer = setInterval(() => {
      console.log('轮询数据...');
    }, 3000);

    onUnmounted(() => {
      clearInterval(timer); // 关键!组件卸载时清除
      timer = null;
    });
  }
}

React useEffect 清理

// React - useEffect return 清理副作用
import { useEffect, useState } from 'react';

function PollingComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const timer = setInterval(async () => {
      const res = await fetch('/api/data');
      setData(await res.json());
    }, 3000);

    // 清理函数:组件卸载时执行
    return () => {
      clearInterval(timer); // 关键!
    };
  }, []);
}

构建报错排查

构建报错就像手机App开业前检查不合格 —— 组件过期了(依赖版本冲突)、配方写错了(语法错误)、厨房太大塞不进(包太大)。不要慌,看报错信息!

npm install 失败

# 1. 清缓存
npm cache clean --force

# 2. 删 node_modules 和 lock 文件重来
rm -rf node_modules package-lock.json
npm install

# 3. 换源(国内网络问题)
npm config set registry https://registry.npmmirror.com

# 4. 特定包安装失败 → 看是不是需要编译的原生模块
# node-sass / sharp / canvas 等 → 用对应系统的预编译版本

TypeScript 类型报错

// 方案1:临时忽略(不推荐长期使用)
// @ts-ignore
obj.someMethod();

// 方案2:正确的类型修复
// 先看报错信息,理解类型不匹配的原因
// 常见问题:
// 1. 类型断言过度 → 用类型守卫代替
// 2. any 传染 → 从源头定义正确类型
// 3. 第三方库缺类型 → npm install @types/xxx
// 4. 泛型推导失败 → 手动指定泛型参数

打包体积过大

// vite 配置 rollup-plugin-visualizer 分析包体积
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({
      open: true,
      filename: 'dist/stats.html',
    })
  ]
});

// 常见优化:
// 1. moment.js → 换 day.js(体积从 200KB+ 降到 2KB)
// 2. lodash → 用 lodash-es 按需引入
// 3. 未使用的代码 → 配置 tree-shaking
// 4. 大依赖 → 考虑 CDN externals + script 加载

常见构建报错速查

Module not found → 路径写错了或没安装这个包。检查 import 路径和 node_modules

SyntaxError → 语法错误。看报错的文件和行号,可能是少了括号/分号

ENOENT: no such file → 文件路径不存在。检查大小写(Mac 不敏感但 Linux 敏感!)

Heap out of memory → Node 内存不够。加 NODE_OPTIONS=--max-old-space-size=4096

digit envelope routines::unsupported → Node 17+ 和 OpenSSL 不兼容。加 NODE_OPTIONS=--openssl-legacy-provider

性能排查速查表

Lighthouse 使用方法

F12 打开 DevTools
切到 Lighthouse 面板
勾选分类 → 点击分析

Lighthouse 会给出 5 个维度的评分:

Performance(性能)→ 加载速度

Accessibility(无障碍)→ 辅助功能

Best Practices(最佳实践)→ 安全和规范

SEO(搜索引擎优化)→ 可搜索性

PWA(渐进式 Web 应用)→ 离线可用性

Core Web Vitals 指标含义和优化方向

LCP(Largest Contentful Paint)= 客人等到第一杯手机App的时间。1/2/4 内装杯=好/需改进/差。优化:压缩图片、预加载关键资源、CDN 加速。

FID / INP(First Input Delay / Interaction to Next Paint)= 打开App后多久开始做手机App。50/100/200ms=好/需改进/差。优化:减少 JS 阻塞主线程、代码分割、延迟加载非关键脚本。

CLS(Cumulative Layout Shift)= App界面不要乱跳!客人正要打开App,App界面突然位移了。0.05/0.1/0.25=好/需改进/差。优化:图片/视频设固定宽高比、避免动态插入内容、字体预加载。

指标含义需改进优化方向
LCP 最大内容绘制时间 ≤2.5s ≤4s >4s 压缩图片、预加载、CDN
INP 交互响应延迟 ≤200ms ≤500ms >500ms 减少主线程阻塞、代码分割
CLS 累积布局偏移 ≤0.1 ≤0.25 >0.25 固定宽高、预留空间

排查 Checklist(照着走)

白屏排查 Checklist:

打开 DevTools Console,看有没有红色报错

打开 DevTools Network,看 JS/CSS 文件是否全部加载成功(状态码 200)

看 HTML 里有没有 <div id="app"></div>(挂载点存在吗)

如果是 SPA 路由问题,刷新后白屏 → 检查 history 模式和 nginx 配置

检查 CSP 策略:Console 有没有 Refused to load 报错

检查 publicPath / base 配置:资源路径是否正确

如果是接口报错导致 → 加 ErrorBoundary / try-catch 兜底

接口问题 Checklist:

看 Network 面板:请求发出去了吗?状态码多少?

4xx 错误 → 检查请求参数、URL、Header 是否正确

5xx 错误 → 后端问题,把 cURL 转给后端同事

CORS 报错 → 检查是否配置了代理或 CORS 头

请求超时 → 看是前端 timeout 配置太短还是后端真的慢

数据不对 → 对比 Request Payload 和 Response,定位是前端还是后端的问题

Cookie / Token 没带上 → 检查 withCredentials 或 Authorization Header

性能问题 Checklist:

跑一次 Lighthouse,看具体得分和建议

LCP 慢 → 检查最大元素(通常是图片)是否压缩、是否用 CDN

INP 慢 → Performance 面板看长任务,减少主线程阻塞

CLS 高 → 检查图片/广告位是否有固定尺寸

首屏慢 → 检查是否做了代码分割(路由懒加载)

内存持续增长 → Memory 面板拍快照对比,排查泄漏

包体积大 → 用 rollup-plugin-visualizer 分析,按需引入依赖

最重要的一条经验:遇到前端问题先打开 DevTools!90% 的问题 Console + Network + Elements 三个面板就能定位。不要上来就改代码猜答案,先看报错信息说什么。