场景
把一个 React + Vite + TypeScript 项目部署到 Cloudflare Pages (Git integration 自动构建模式), 上传 push 触发构建日志一直炸:
npmci npm run build
▎ vite build
sh: 1: vite: not found npm ERR! Lifecycle "build" didn't pass "exec".
错误 vite: not found 但本地 npm run build 一切正常, GitHub Actions CI 也正常. 怎么 CF Pages 独立环境挂?
排查一路, 最后根因有点意外: CF Pages 默认环境变量 NODE_ENV=production, 而 npm ci 在此模式下会跳过 devDependencies, 导致 vite 根本没装.
排查过程
被 "vite: not found" 误导阶段
一开始以为:
- CF Pages 没装 Node 或版本太低 --- 排查, Node 20 OK
package.json缺 vite 依赖 --- 检查package.json:
json
"devDependencies": {
"vite": "^5.2.11",
"husky": "^9.0.11",
"@vitejs/plugin-react-swc": "^3.7.0",
"vitest": "^1.6.0"
}
vite 在 devDependencies. 一般情况下 npm ci 会装 devDeps. 但是...
关键诊断: packages 数量不对
让构建日志输出 packages 总数:
$ npm ci
added 449 packages in 12s
449 个 packages. 而本地 npm install 后看 node_modules:
$ ls node_modules | wc -l
1019
差 570 个 --- 配 devDeps 全没了.
真凶: NODE_ENV=production 会让 npm 跳过 devDependencies
查 npm 文档 npm-install docs (https://docs.npmjs.com/cli/v9/commands/npm-install):
▎ If NODE_ENV is set to production at install time, npm will not install modules listed in devDependencies.
CF Pages 默认注入 NODE_ENV=production, 触发 npm 这个优化分支 → devDeps 跳过 → vite 没装 → build 时 vite build 找不到 binary → 报
vite: not found.
而 GitHub Actions CI 默认 NODE_ENV 不设, Vercel 默认 NODE_ENV 不设, EdgeOne 默认也不设, 独独 CF Pages 默认生产环境变量化.
这就解释同样 lockfile 同样 package.json, 仅 CF 炸.
三个解决方案
方案 A: 给 CF Pages 加 NODE_ENV=development
CF Pages 项目 → Settings → Environment Variables → 加一条 NODE_ENV = development.
缺点:整个构建环境全程 NODE_ENV=development, vite build 时某些 optimization 不会触发 (例如 mode=production 默认逻辑), build 出
bundle 没压缩. 不推荐.
方案 B: 给 CF Pages 加 NPM_CONFIG_PRODUCTION=false
CF Pages 项目 → Settings → Environment Variables → 加一条 NPM_CONFIG_PRODUCTION=false. 同样语义但比 A 干净, 不污染 NODE_ENV.
生效: npm 会忽略 production 模式, 装 devDeps. 但整个环境标记也会让其他 tooling (不检 NODE_ENV 直接检 npm_config_production)
误判以为开发模式.
中等推荐. 临时方案.
方案 C: 加 .npmrc 配置 include=dev ✅ (推荐)
最干净. 项目根新建 public/.npmrc 或仓库根 .npmrc:
include=dev
.npmrc 中 include=dev 强制 npm不论 NODE_ENV 都装 devDependencies. 不动 CF Pages 环境变量, 不污染 NODE_ENV. 也不影响本地开发 ---
本地 NODE_ENV 通常不设, npm 默认装 devDeps.
CF Pages 二次构建, packages 数从 449 涨到 1019:
$ npm ci
added 1019 packages in 22s
vite, husky, vitest 全装上. npm run build 跑通 365 个静态文件生成, CF Pages 上传成功.
补充坑
坑 1: prepare 脚本里的 husky 找不到
package.json:
"prepare": "husky install"
npm 在生产环境自动跑 prepare 钩子, 但 husky 也没装 (husky 也是 devDep). 改:
"prepare": "husky install || true"
加 || true 让 husky 找不到也不阻断构建.
坑 2: binlink 缺失 / alternative binary 找不到
部分 CF Pages 版本上 npm 装了 vite 但 node_modules/.bin/vite 软链丢了, npm run build 调 vite 仍找不到. 临时解决:
"scripts": {
"build": "tsc && vite build",
"build:cf": "node node_modules/vite/bin/vite.js build"
}
build:cf 直接走 node node_modules/vite/bin/vite.js 入口 JS, 绕过 bin 软链. CF Pages 后台 Build Command 设 npm run build:cf.
但随着 .npmrc 修复 sort 出来后 binlink 也正常, 这条不必再加. 留下笔记备查.
坑 3: package-lock.json 顶部 name 必须跟 package.json 匹配
老 fork 项目 package.json 改过 name 但 lockfile 没重生, CF Pages 报 ERR_PNPM_OUTDATED_LOCKFILE 或类似.
重生 lockfile:
npm install --package-lock-only
git add package-lock.json
git commit -m "chore: regenerate package-lock.json"
git push
CF Pages 重新构建 lockfile 跟 package.json 同步.
结论
CF Pages 默认 NODE_ENV=production 对常规 React/Vite 工程是个陷阱:
┌───────────────┬────────────────┬──────────────────────────┐
│ 阶段 │ 行为 │ 问题 │
├───────────────┼────────────────┼──────────────────────────┤
│ npm ci │ 跳过 devDeps │ vite, vitest, husky 不装 │
├───────────────┼────────────────┼──────────────────────────┤
│ npm run build │ 调 vite binary │ not found │
├───────────────┼────────────────┼──────────────────────────┤
│ 上传 │ 静态文件 0 │ 部署失败 │
└───────────────┴────────────────┴──────────────────────────┘
加项目根 .npmrc 一行 include=dev 解决, 不污染环境变量, 不改 build 命令.
示例项目: ToolKit (https://xbot.ink) --- 在 CF Pages 部署的项目, 100+ 免费在线工具 + 10 语言.
扩充源码 (含 .npmrc, _redirects, CF 部署配置): (https://github.com/crazy-choose/toolkit-additions)