开发排错指南
报错不可怕,可怕的是看着报错发呆 20 分钟,然后默默关掉终端假装什么都没发生。
说实话我当初也是 console.log 大法走天下,哪里不对就 console.log('here???'),一个文件插十几个,最后自己都分不清哪个是哪个。后来才慢慢摸索出一套排错的方法论——不是说 console.log 不好用,而是有更快的路子。
这份指南把实际开发中踩过的坑、常见的报错、好用的工具整理到了一起。不管你是刚入门还是已经写了一段时间代码,应该都能找到有用的东西。
排错黄金法则
排错说白了就是个缩小范围的过程。很多人一看到报错就开始瞎改代码碰运气,其实静下心来按步骤走反而更快。我自己总结下来就这么几个关键动作:
先把报错信息读完整。这话听着像废话对吧,但真的很多人看到一堆红字就慌了。报错信息一般会告诉你错误类型(TypeError、SyntaxError 这些)、具体描述、还有出错位置。有个小技巧:从最后一行开始往上看,最底下的 stack trace 通常才是你自己写的代码,上面都是框架内部调用链。
然后就是复现。能稳定复现的 bug 就已经解决了一半。问自己:每次都出现还是偶发?什么操作之后触发的?换个浏览器试过没?如果不能复现,先别急着修,把当时的环境和步骤记下来。
接下来二分法缩小范围——注释掉一半代码看问题还在不在,重复几次就能定位到具体哪几行。找到问题之后先想清楚根因再动手改,头痛医头的方案可以临时用,但记个 TODO 别让它变成永久方案。最后别忘了回去把复现路径再走一遍验证,顺便想想有没有相关的 edge case。
前端常见报错
CORS 错误
Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy
原因:浏览器的同源策略,前端域名和 API 域名不一致。
我之前有个项目,前后端分开部署,花了整整一下午才搞明白 CORS 到底在拦什么。当时后端明明设了 header,但 preflight OPTIONS 请求一直返回 403——最后发现是 Nginx 那层把 OPTIONS 请求直接拦掉了,根本没到后端。这种多层代理的情况特别容易踩坑。
怎么解决:开发环境在 next.config.js 或 vite.config.ts 里配 proxy 就行。后端设置 Access-Control-Allow-Origin header,但注意不要用 * 通配符上生产,指定具体域名。
Cannot read properties of undefined
TypeError: Cannot read properties of undefined (reading 'map')
这个错应该是前端出现频率最高的了。说来惭愧,我到现在偶尔还会犯——上次写了个组件,API 数据还没返回就开始 .map() 渲染列表,开发环境网速快没问题,一到测试环境网速慢了直接白屏。花了二十分钟才意识到就是少了个 loading 判断。
本质上就是访问了 undefined 或 null 的属性。用 optional chaining data?.items?.map(...) 或者给默认值 const items = data?.items || [],再加个 loading 状态判断就好了。
Module not found
Module not found: Can't resolve 'xxx'
依赖没装、路径写错、或者大小写不对。macOS 不区分大小写但 Linux 区分,这个坑在部署时特别常见——本地跑得好好的,CI 一跑就挂。bun install 重新装依赖,检查 import 路径大小写,实在不行删掉 node_modules 和 lock 文件重来。
Hydration Mismatch (Next.js)
Text content does not match server-rendered HTML
原因:服务端渲染的 HTML 和客户端渲染的不一致。常见于使用了 Date.now()、window 对象、或者随机数。
解决:
- 用
useEffect处理只在客户端运行的逻辑 dynamic(() => import(...), { ssr: false })禁用特定组件的 SSR- 检查是否有 browser-only API 在服务端执行了
Maximum update depth exceeded
Error: Maximum update depth exceeded
组件无限循环渲染了。十有八九是 useEffect 依赖数组写错,或者在 render 过程中直接调了 setState。检查依赖数组是不是每次渲染都创建了新的引用(比如在 render 里 new Object())。还有一个经典错误:onClick={setCount(count + 1)} 少了箭头函数包裹,应该写 onClick={() => setCount(count + 1)}。
白屏 / Chunk Load Error
ChunkLoadError: Loading chunk xxx failed
部署新版本后,用户浏览器缓存了旧的 chunk 文件名,但服务器上已经换成新的了。加个全局错误边界 catch 住这个错误后自动刷新页面,再配合合理的缓存策略基本就能解决。
样式不生效 & React Key Warning
样式问题原因千奇百怪:CSS 优先级、class 名冲突、styled-components 的 SSR 配置没弄对。排查时打开 DevTools Elements 面板看样式有没有被删除线划掉(说明被更高优先级覆盖了),styled-components 记得配 ServerStyleSheet。
至于 Each child in a list should have a unique "key" prop 这个警告,用数据的唯一 ID 当 key 就行,别用数组 index——尤其是列表会增删排序的情况下,index 当 key 会导致渲染异常。
环境变量和类型错误
Next.js 里客户端能访问的环境变量必须以 NEXT_PUBLIC_ 开头,服务端变量不需要这个前缀。改完 .env 文件记得重启 dev server,这一点很容易忘。
TypeScript 类型错误碰到 Type 'string' is not assignable to type 'number' 这种,别急着加 as any。as any 是在欺骗编译器,bug 会在运行时找你算账。先想想是不是数据结构真的和预期不一样。
后端常见报错
EADDRINUSE (端口被占用)
Error: listen EADDRINUSE: address already in use :::3010
# 找到占用端口的进程然后杀掉
lsof -i :3010
kill -9 <PID>
ECONNREFUSED / 500 Internal Server Error
ECONNREFUSED 127.0.0.1:27017 就是数据库或目标服务没启动,检查 MongoDB / Redis 是否在跑。
500 是个万金油错误码,意思就是"后端炸了但不告诉你为什么"。前端只拿到 500 是正常的(敏感错误不应该暴露给客户端),排查得去看后端日志,错误详情一定在那里。
401 / 403 认证问题
- 401 Unauthorized:没带 token 或 token 过期了
- 403 Forbidden:token 有效但没权限
用 DevTools Network 面板看 request headers 里有没有 Authorization: Bearer xxx。JWT 相关的 jwt malformed 或 jwt expired 也归这一类,检查 token 有没有被截断,过期了就重新登录。
MongoDB Duplicate Key Error
E11000 duplicate key error collection
插入了重复的 unique 字段值。检查是不是有脏数据,或者 unique index 建错了。这个在批量导入数据的时候特别常见。
Mongoose Validation Error
ValidationError: xxx: Path `xxx` is required
必填字段没传,对照 Schema 定义检查请求体就行。简单但容易忘——尤其是后来新加的 required 字段,老的测试数据就会挂。
Memory Leak / Heap Out of Memory
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
临时方案 NODE_OPTIONS=--max-old-space-size=4096 能顶一阵,但治标不治本。根本上要排查内存泄漏——常见原因是没清理的 setInterval、事件监听器忘了 removeListener、或者大数组不断往里 push 没有上限。
Module 循环依赖 & Timeout
NestJS 里两个 module 互相 import 会导致 Cannot read property 'xxx' of undefined。用 forwardRef(() => XxxModule) 打破循环,或者重新设计模块依赖。
Timeout 错误一般是数据库查询太慢、外部 API 响应慢、或者死循环,给关键操作加耗时日志找出瓶颈在哪一步。
AI 辅助排错
ChatGPT 和 Claude 是排错利器,但很多人用法不对,丢个截图过去问"这是什么错",得到的回答也是泛泛而谈。
高效提问模板
我在做 [具体功能],用的技术栈是 [框架版本]。
执行 [具体操作] 时出现了这个错误:
[完整报错信息,不要截图,复制文字]
我已经试过:
1. [你尝试过的方案 1]
2. [你尝试过的方案 2]
相关代码:
[贴出关键代码片段,不要贴整个文件]
几个技巧
- 贴文字不要贴截图:AI 解析文字比解析图片准确得多
- 说清版本号:React 18 和 React 19 的解决方案可能完全不同
- 说明你试过什么:避免 AI 重复建议你已经试过的方案
- 给上下文:是 Next.js 的 App Router 还是 Pages Router?是 NestJS 还是 Express?这些都会影响答案
排错工具链
浏览器 DevTools
最强大的前端调试工具,几个必会的面板:

- Console:看报错和日志
- Network:看请求状态码、响应内容、请求耗时
- Elements:看 DOM 结构和 CSS 样式
- Application:看 localStorage、cookies、缓存
- Sources:打断点,比 console.log 精准多了
VS Code Debugger
配置 launch.json 之后可以直接在编辑器里打断点调试 Node.js 代码,能看到完整的调用栈和变量值。比在代码里到处插 console.log 好用太多。

Postman / Thunder Client
API 调试的标配工具。建议把常用请求保存成 Collection,团队共享。前后端联调的时候,先用 Postman 确认 API 没问题,再排查前端。
终端日志
后端排错第一步永远是看日志。NestJS 的 Logger 已经很好用了,别嫌麻烦,关键操作都加上日志:
this.logger.log(`Creating user: ${email}`);
this.logger.error(`Failed to create user: ${error.message}`, error.stack);
最容易翻车的排错习惯
我自己就经常犯第4条的毛病,改了代码忘了保存,然后对着旧的输出纳闷半天。下面这几条你看看有没有中招的:
只看第一行报错。很多报错连续输出好几十行,真正有用的信息可能在中间或最后。尤其是 stack trace,你自己写的代码一般在最下面,上面都是框架内部的调用链。
不看版本号。"我按教程写的,为什么不行?"——因为教程用的 v2,你装的 v3,API 早就变了。永远先检查 package.json。
盲目抄 Stack Overflow。上面的回答可能是 2018 年写的,那个年代的方案现在可能早过时了。看的时候留意发布时间和评论区有没有人说"这个已经不行了"。
改了代码没保存 / 没重启。经典中的经典。改了后端代码忘了重启 dev server,改了 .env 没重启,改了配置但看的还是旧缓存。我有一次 debug 了快一个小时,最后发现改的那个文件根本没 Cmd+S……
多个问题同时改。一次只改一个地方,验证完再改下一个。同时改了三个地方然后好了,你都不知道到底是哪个改动修复了问题。
不用版本控制。改代码前先 git stash 或者开个新分支。万一改崩了,git stash pop 就能回到之前的状态。别让一次排错变成两个 bug。
相关资源
- VS Code 插件推荐 — 开发效率翻倍的插件清单
- Cursor 使用指南 — AI 编辑器的排错技巧
- MDN Web 调试指南
- Node.js 调试文档
- Chrome DevTools 文档