项目踩坑复盘
把每一次熬夜排查的 bug,按"现象 → 排查 → 根因 → 方案 → 预防"五步还原成文档。下次再遇到,闭眼也能修好。
从现象到预防的闭环
复盘不是写"我犯了错",而是把一次 bug 拆成可复用、可教学、可预防的检查项。
记录"发生了什么"
先客观描述:操作步骤、预期结果、实际结果、报错信息、复现条件。不掺杂判断,只写事实。
列出"我试过什么"
把每一步排查动作记下来,包括失败的尝试。失败路径同样宝贵——它告诉你这条路不通。
回答"为什么会这样"
连问五次 why,直到挖到机制层面的原因,而不是停留在"我改了某行就好了"的表象。
给出"怎么修的"
记录修复前后的代码对比、决策依据。如果有多个候选方案,写下为什么选这个而不是另一个。
设计"怎么不再犯"
把根因转化为可执行的检查项:lint 规则、单元测试、Code Review 清单、文档补充。
PITFALL · 案例库
每一个坑,都是写给未来自己的备忘录
—— 三亩地 · 复盘守则
异步循环里的闭包陷阱
2026 年 7 月在做一个批量请求节流功能时踩到的经典老坑,记录于此,永不再犯。
JS · 闭包
循环里写 setTimeout,输出全是最后一个值
需求:给一组接口做节流请求,每个请求间隔 200ms。结果控制台打印出来的下标全是 5——典型的闭包共享变量问题。
// ❌ 报错写法:所有定时器共享同一个 i for (var i = 0; i < 5; i++) { setTimeout(() => { console.log('请求下标:', i); }, i * 200); } // 输出:5 5 5 5 5(而非 0 1 2 3 4)
FIX · 修复
var 声明的变量被提升到函数作用域
var 没有块级作用域,所有回调共享同一个 i。等到定时器真正执行时,循环早已结束,i 已经变成 5。换成 let 立即修复,因为 let 每次循环都会创建一个新的绑定。
// ✅ 推荐写法:let 创建块级作用域绑定 for (let i = 0; i < 5; i++) { setTimeout(() => { console.log('请求下标:', i); }, i * 200); } // 输出:0 1 2 3 4 // 预防:项目 ESLint 启用 no-var 规则
提交前过一遍这八条
从历次复盘里提炼的高频踩坑点,写成 check 后只要 30 秒就能扫一遍。
异步循环用 let 不用 var
凡是有 await / setTimeout / Promise 的循环,变量声明一律改为 let 或 const。
接口返回值先校验再使用
不要直接 res.data.list.forEach,先判空、判长度、判类型,防御性编程从入口做起。
提交前看 diff 不只看代码
很多 bug 来自一个误删的空行、一个未保存的文件。Git diff 是最后一道防线。
缩进与换行统一走 Prettier
不要靠手感调格式,工具一键搞定,避免空格与 Tab 混用引发的诡异问题。
依赖升级先看 changelog
不要无脑 npm update,先扫一眼 BREAKING CHANGES,能省下整晚的排查时间。
状态修改走不可变写法
Redux / React / Pinia 都依赖引用对比,直接 push / splice 不会触发更新。
环境变量分 dev / prod
永远不要在生产里 hardcode 本地 IP,配置走 .env 与构建时注入。
敏感信息绝不进代码库
密钥、Token、数据库密码统一走环境变量与密钥管理服务,Git 历史一旦泄露无法回收。
踩过的坑,写下来才不算白踩;写下来的坑,分享出去才算真正修好了。