经典痛点:本地 npm run dev 一切正常,部署到 Vercel / Netlify / Fly.io / Render,要么 build fail、要么 runtime crash、要么页面看似加载完成但功能全坏。“在我电脑上能跑”(works on my machine)是个老梗,但根因永远是同一类:本地的隐式假设在生产环境不成立。
最有效的修法不是”猜哪里不一样”,而是系统化 diff 本地 vs 生产的几个维度:env vars / runtime version / file system / network / build process。
常见原因
按命中率从高到低:
1. 环境变量在生产没设
最高频。你本地 .env 里有 DATABASE_URL、STRIPE_SECRET_KEY,但部署平台没配,运行时 process.env.X === undefined。
如何判断:生产日志报 Cannot read property X of undefined 或 Invalid URL 类。
2. Node / Python 版本不一致
本地 Node 22,生产 Node 18(平台默认)。某些 ES2023+ 语法或新 API(如 Array.prototype.toSorted)在 18 上不存在,runtime 崩。
如何判断:生产日志报 X is not a function 或 SyntaxError。
3. 文件系统差异(大小写、路径分隔符)
// macOS / Windows:大小写不敏感
import './components/Button'; // ✅ 即使文件名是 button.tsx
// Linux 生产:大小写敏感
import './components/Button'; // ❌ Cannot find module
如何判断:生产 build 报 “Module not found” 但本地正常。
4. 依赖本地服务(文件系统、内存)
本地用 fs.writeFile('./uploads/x.jpg') 存上传,部署到 serverless 平台(Vercel / Cloudflare Workers)文件系统是 read-only,写入直接报错。
如何判断:报 EROFS: read-only file system 或类似。
5. dev 服务器和生产 build 行为不同
Vite / Webpack dev 模式有 HMR、相对宽松;生产 build 做 tree-shake、minification、SSR。dev 跑得通的代码 build 后可能挂。
如何判断:本地 npm run build && npm start 看是否复现。
6. CORS / cookie domain / 第三方 API key
localhost:3000 跟 yourdomain.com 受不同的 CORS、cookie SameSite、third-party API allowlist 限制。dev 时 OAuth 用了 test key,prod 还指着 test key。
如何判断:生产 Network 报 CORS error / 401 / 403 但 dev 正常。
7. timezone / locale
本地 date.toString() 出”Beijing time”,生产 server 是 UTC——所有时间显示差 8 小时。
如何判断:时间相关功能在生产怪。
最短修复路径
Step 1:本地跑生产 build
90% 的”local works, prod doesn’t” 都能通过这步复现:
# Next.js
NODE_ENV=production npm run build && npm start
# Vite
npm run build && npm run preview
# 通用
NODE_ENV=production node server.js
如果本地 prod build 也挂 = 不是部署问题,是 build / 代码问题。直接 fix。
Step 2:diff env vars
# 列出本地 .env
cat .env | sort > /tmp/local-env.txt
# 列出生产 env(从平台 dashboard 复制)
echo "DATABASE_URL=..." > /tmp/prod-env.txt # 手动整理
sort /tmp/prod-env.txt > /tmp/prod-env-sorted.txt
# diff
diff /tmp/local-env.txt /tmp/prod-env-sorted.txt
补齐缺失的:
# Vercel
vercel env add DATABASE_URL production
vercel env add STRIPE_SECRET_KEY production
# 或在 dashboard UI 操作
Step 3:锁 runtime 版本
// package.json
{
"engines": {
"node": "20.x"
}
}
Vercel / Netlify 会读 engines.node。Cloudflare Pages 在 Build settings 里设 NODE_VERSION=20。
确认 .nvmrc / .node-version 文件:
20.10.0
Step 4:fix 大小写
# 在 mac 上找所有 import,确认大小写
grep -r "from './" src/ | sort -u | head -50
# 对照实际文件名 ls -la src/components/
强制 macOS Volume 大小写敏感(极端但彻底):
diskutil apfs createContainer disk0s2
# 创建大小写敏感分区,把项目放过去
或加 eslint 规则 import/no-unresolved + import/case-sensitive。
Step 5:替换本地依赖
本地用 → 生产改用
================================================
fs writeFile → S3 / R2 / Cloudinary
in-memory cache → Redis (Upstash / Cloudflare KV)
SQLite → Postgres / Turso
local cron job → Vercel Cron / Inngest
process.env at boot → 远程 config service / Vault
Step 6:dev / prod 行为差异
// 永远不要在 dev / prod 分支不同逻辑
if (process.env.NODE_ENV === 'development') {
// ❌ 这样 dev 测不到生产 bug
}
// 改成
const useMock = process.env.USE_MOCK === 'true';
// 然后 dev 和 staging 都跑 USE_MOCK=false 验证
Step 7:UTC + 显式 locale
// server 端永远 UTC
const date = new Date(); // 用 ISO 字符串传输
// client 端显示时用 Intl
new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai' }).format(date)
预防建议
- 每次推 PR 前本地跑一次
npm run build && npm start,不能只信 dev server - env vars 用
zod在 boot 时校验,缺失立刻 fail-fast(不要等 runtime) - package.json 锁
engines.node,CI 用actions/setup-node@v4 with: node-version-file: .nvmrc - Mac 用户开 Linux Docker 容器跑 build,模拟生产文件系统大小写敏感
- 任何”只在本地能用”的代码(fs.write、in-memory state)从 day 1 就用云服务替代
- 用一个 preview / staging 环境跟 prod 配置一致,PR merge 前在 preview 验证
- 时间永远存 UTC ISO 字符串,显示层才转 timezone
- 全公司用同一个 .nvmrc / .python-version,避免”我电脑能跑”