Monorepo 指在一个仓库中管理多个项目或包。它不是把多个 node_modules 搬进同一个文件夹,也不等于微前端。它的价值在于让跨项目修改可追踪、让依赖关系可验证;如果没有边界,一个仓库只会让相互引用变得更方便。
从两个应用和一个共享包开始
apps/
web/
admin/
packages/
ui/
domain/
config/
pnpm-workspace.yaml
web 和 admin 是交付入口;ui 放展示组件;domain 放不依赖浏览器或框架的业务规则;config 放共享工具配置。包的拆分应对应稳定职责,不能仅因为某个文件被使用两次就制造一个新包。
packages:
- 'apps/*'
- 'packages/*'
应用通过包名依赖共享能力。例如应用的 package.json 可以声明下面的依赖;这只是配置片段,还需要在 ui 包中声明同名 name 和明确的导出入口。
{
"dependencies": {
"@cai/ui": "workspace:*"
}
}
workspace 协议要求使用本地 workspace 包,避免无意解析到远程同名包。不要绕过入口直接引用另一个应用的 src 内部文件。
依赖方向决定可维护性
让应用依赖包,让 UI 依赖稳定的 domain 接口;domain 不反向引用页面、路由和浏览器全局对象。React 和 Vue 展示层可以各有适配包,共享规则与类型,通常不应该强行共享渲染组件。
共享 React 组件库还要考虑 peerDependencies 和构建外置规则,避免把另一份 React 打入应用。包采用源码消费还是构建产物消费,也要明确约定,不能让编辑器能解析但部署产物缺少文件。
Workspace 不自动解决任务编排
pnpm 管理工作区与依赖;Turborepo、Nx 等工具可以进一步管理任务图和缓存。若应用构建依赖共享包产物,就要先构建该包,或者由应用构建器明确承担源码编译。
缓存键必须覆盖影响产物的输入:源码、锁文件、工具配置以及参与构建的环境变量。缓存命中却部署了错误 API 地址,通常说明输入定义有缺口。私钥和真实令牌不应进入缓存产物或日志。
CI 从受影响范围出发
修改共享 UI 包需要验证它的消费者;只运行该包的单元测试,发现不了应用导入和样式冲突。影响分析需要真实依赖图支持,初期不确定时宁可运行相关应用构建,再逐步优化。
对于发布到包仓库的库,可以选择独立版本或统一版本,并记录变更;只供仓库内应用消费的包,不一定需要每次发布到外部仓库。应用是否独立部署是另一层决策。
迁移时保留可工作的基线
先迁移一个依赖少的公共包,跑通安装、类型检查、构建和消费者验证,再迁移第二个包。不要同时更换包管理器、重写构建器和重组所有业务目录,否则失败时很难确定是哪一步改变了行为。