跳到主要内容

Monorepo 与多环境发布

多包放在一个 Git 仓库里(monorepo),以及 dev / 测试 / 生产 的配置怎么进前端产物,是全栈最常踩的工程坑。工作区依赖 包管理;构建 Vite;密钥只能留服务端,见 Node 与浏览器运行时。容器侧发布见 Docker 与 灰度发布。

解决什么问题​

痛点做法
前端、组件库、Node 服务各一个仓,改接口要跨三个 PR工作区:一次改、一次 CI
../../pkg 相对路径复制workspace: 协议引用内部包
生产打进了测试 API 地址分环境 env;分清构建时注入 vs 运行时配置
密钥出现在 bundle前端只能拿公开配置;密钥走后端

一句话:内部包用工作区链接;前端环境变量默认在构建时写死,当配置而不是当密钥库。

Monorepo 先认现场​

标志含义
pnpm-workspace.yamlpnpm 工作区
package.json 的 workspacesnpm / yarn 工作区
nx.json / turbo.json任务编排与缓存(Nx / Turborepo)
多个 package.json 在 packages/、apps/典型布局
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
{
"dependencies": {
"@acme/ui": "workspace:*"
}
}

pnpm install 把内部包链到一起,改 packages/ui 立刻被 apps/web 看到(还取决于构建:源码直接引用 vs 要先 build 内部包)。TS 要用 项目引用 或打包器 alias 指到源码,否则会引用过期的 dist/。

内部包形态何时
纯 TS 源码,应用直接 alias中小仓,迭代快
先 build 再给应用发布到 registry、或需单独产物
发 npm 的公开包另有版本与 changelog,不只是 workspace

不要把整仓打成一个巨大 vite 应用假装 monorepo——边界是 可独立发布或可独立测试的包。

Turborepo / Nx 解决的是「谁依赖谁、哪部分要重跑」的任务图,不是第二种包管理器。先有 workspace,再视 CI 时长引入。

环境变量:构建时 vs 运行时​

浏览器里没有安全的 process.env 运行时读取密钥的方式。Vite 默认:

# .env.development / .env.production / .env.local(后者 gitignore)
VITE_API_BASE=https://api.example.com
const base = import.meta.env.VITE_API_BASE;

只有 VITE_ 前缀(可配 envPrefix)会进客户端。打进去的是 构建那一刻 的字符串。

模式行为适合
构建时注入vite build --mode staging 把 URL 写进 JS静态托管、每环境打一次包
运行时配置容器里的 config.js 或接口下发,应用启动再读同一镜像 多环境(K8s ConfigMap)
后端 BFF浏览器只打相对路径 /api,网关分流少暴露环境差异

同一 Docker 镜像要进测试和生产时,不要为每个环境重新 vite build 才改 API 域名——用运行时配置或反向代理同源。镜像构建见 Dockerfile 与镜像构建。

.env.local、密钥、.pem 不要进 Git。前端仓库里出现 SECRET、PRIVATE_KEY 就是事故。

发布清单(前端静态资源)​

pnpm install --frozen-lockfile
pnpm lint && pnpm typecheck
pnpm build
把 dist/ 交给 Nginx / CDN / 对象存储
HTML 短缓存或不缓存;带 hash 的 assets 长缓存

多环境:

  • 静态站:每环境一次 build(mode 不同)或运行时 window.__CONFIG__
  • 容器:Nginx 镜像 + 启动脚本替换配置,或 Sidecar 下发

回滚 = 换回上一份 dist 或上一镜像 tag,所以产物和镜像都要 打 tag 留存。灰度见后端面试专题,前端配合的是:旧 HTML 不要长期指到已删的 hash 文件。

要点​

  1. 一种包管理器 + workspace:,内部包不要用复制粘贴
  2. 应用引用内部包时想清楚走源码还是走 dist
  3. VITE_* 会进客户端,构建时就定死
  4. 同一镜像多环境 → 运行时配置或反向代理,不要靠重新打包塞域名
  5. 密钥永不进前端环境文件

面试速记​

  1. Monorepo 是什么? 一个仓库多个 package,用工作区链接内部依赖。
  2. 和多仓比的代价? CI 变复杂、权限边界糊;要用任务图做出增量构建才划算。
  3. Vite 环境变量何时写进代码? 构建时;不是 Node 服务器每次请求读。
  4. 为什么前端 .env 装不了真正的密钥? 产物在用户磁盘上,任何人都能打开 JS。
  5. 静态资源和容器配置怎么配合? 要么每环境构建,要么构建一次、启动时注入公开配置。