架构)
Monorepo单体仓库架构你把多个项目放在一个.git仓库里统一管理 —— 这只是 Monorepo 架构的起点但是还不是Monorepo架构因为它缺乏Monorepo的工程加载没有共享依赖、没有跨项目引用。如果再引入pnpm workspace Turborepo等工具就升级为现代化、高效率的 Monorepo 工程体系。❗ 注意不是所有“多项目同仓”都叫“规范的 Monorepo”如果你只是简单地把几个不相关的项目丢进一个带.git的文件夹比如作业homework1/,homework2/而没有使用工作区workspace工具、没有共享依赖、没有跨项目引用那它只是一个“物理上的单仓库”但缺乏 Monorepo 的工程价值。真正的Monorepo 架构通常还会配套以下实践能力工具示例依赖管理pnpm workspace/yarn workspaces任务编排与缓存Turborepo/Nx按变更构建nx affected:build/turbo run --filter...版本发布changesets/lerna对比其他模式模式Git 仓库数项目隔离性协作效率典型场景Monorepo1 个低代码可见高原子提交、共享依赖大型产品、组件库生态、微前端Multi-repoN 个高完全独立低跨仓修改繁琐独立产品线、开源项目集合Git Submodule/Subtree1 主 N 子中子模块独立中管理复杂嵌入第三方库、可选模块Monorepo 根配置共享模式在 monorepo 项目中将基础配置文件如 vite.config.ts、tsconfig.json放在根目录子包引用并扩展这些基础配置。本项目结构my-monorepo/ ├── vite.config.ts ← 基础配置getBaseConfig └── packages/ ├── my-design/ │ └── vite.config.ts ← 导入并扩展 getBaseConfig ├── my-lib/ │ └── vite.config.ts ← 导入并扩展 getBaseConfig └── my-hooks/ └── vite.config.ts ← 导入并扩展 getBaseConfig被引用的子包packages/my-design/vite.config.tspackages/my-lib/vite.config.tspackages/my-hooks/vite.config.ts常用模式对比模式说明适用场景根配置共享✓根目录放基础配置子包引用组件库、工具库独立配置每个子包独立完整配置差异化较大的项目继承覆盖子包可覆盖父配置需要灵活定制的项目优缺点优点减少重复: 基础配置写一次子包复用统一规范: 所有子包使用相同的构建/测试配置易维护: 修改一次即可同步到所有子包缺点耦合: 子包依赖根配置独立性降低灵活性差: 子包无法轻易自定义Monorepo 依赖声明理论原则谁用谁声明场景:packages/my-lib/rollup.config.mjs中使用了fs-extra和rimrafimportfsfromfs-extra;import{rimrafSync}fromrimraf;做法:在packages/my-lib/package.json里把它们写到devDependencies构建时使用如果根目录的脚本如scripts/publish.js也用到了fs-extra再在根package.json的devDependencies里单独声明一份。原则:谁import谁在自己的package.json里声明依赖pnpm workspace包依赖管理器是什么pnpm 是一个快速、节省磁盘空间的 npm 包管理器。workspace是 pnpm 内置的Monorepo 支持功能用于在一个仓库中管理多个子项目包之间的依赖关系。解决什么问题问题pnpm workspace 如何解决跨项目依赖安装繁琐通过workspace:*协议直接引用本地包无需npm link或发布到 npm重复安装依赖浪费空间所有依赖存入全局 store通过硬链接共享磁盘占用减少 60%幽灵依赖未声明却能用严格隔离 node_modules未在package.json声明的依赖无法 import版本不一致工作区内包自动使用最新本地版本避免“我这能跑”示例// apps/web/package.json{dependencies:{my/ui:workspace:*// ← 直接链接到 packages/ui 的源码}}本质它管的是 “依赖怎么装、怎么链接”pnpm 使用独特的内容寻址存储Content Addressable Store机制管理依赖核心目录目录位置作用.pnpm-store项目根目录或全局真实存储- 下载的包原始文件node_modules/.pnpmnode_modules/.pnpm/虚拟存储- 包的硬链接入口.pnpm-store/ ← 真实文件内容寻址存储 └── v10/ └── files/ └── ab/ └── adobecss-tools4.3.3... node_modules/.pnpm/ ← 软链接/硬链接入口虚拟目录 └── adobecss-tools4.3.3/ └── node_modules/ └── react - ../../react17.0.1/node_modules/react下载: 包从 npm registry 下载到 store链接:node_modules/.pnpm中创建硬链接指向 store 中的真实文件引用: 项目中的node_modules/xxx通过符号链接指向.pnpm中的包优点节省空间: 同一包同一版本只存储一份速度快: 硬链接是即时操作无需复制可移植: 只要 store 存在node_modules 就能工作Store 位置决定因素优先级顺序项目 .npmrc的store-dir配置全局 ~/.npmrc的store-dir配置环境变量PNPM_HOMEstore子目录pnpm 默认行为→ 全局 store:~/Library/pnpm/store/v10默认全局 store 路径macOS:~/Library/pnpm/store/v10Linux:~/.local/share/pnpm/store/v10Windows:%LOCALAPPDATA%/pnpm/store/v10二、Turborepo任务编排与构建系统 是什么Turborepo 是由 VercelNext.js 背后团队开发的高性能 Monorepo 构建工具用 Rust 编写。它不管理依赖安装而是管理脚本执行build/test/lint/dev。 解决什么问题问题Turborepo 如何解决全量构建太慢增量构建只重新构建受影响的包基于依赖图任务串行执行效率低并行执行无依赖的任务如同时 build ui 和 utilsCI 每次都重做相同工作远程缓存将构建结果缓存到云端团队共享脚本分散难维护统一在turbo.json中定义任务流水线✅ 示例// turbo.json{pipeline:{build:{dependsOn:[^build],// 依赖其他包的 build 结果outputs:[.next/**,dist/**]},test:{cache:false}}}运行# 只构建受影响的包并行执行pnpmturbo run build本质它管的是 “任务怎么跑、怎么缓存”三、必须一起用吗—— 答案不必须但强烈推荐组合场景是否可行优缺点只用 pnpm workspace✅ 可行✔️ 依赖管理优秀❌ 构建/测试仍需手动pnpm -r run build无法增量、无缓存只用 Turborepo⚠️ 部分可行✔️ 任务编排强大❌ 依赖仍靠 npm/yarn/pnpm 自己管理可能有幽灵依赖或空间浪费pnpm Turborepo✅✅ 最佳实践✔️ 依赖干净高效 构建极速智能✔️ 社区公认现代化 Monorepo 黄金组合不用任何工具❌ 不推荐多项目同仓但无自动化退化为“物理单仓”失去 Monorepo 优势四、类比理解想象你在管理一个大型工厂Monorepopnpm workspace物流与仓储系统→ 负责把原材料npm 包精准配送到每个车间子项目不重复进货不乱堆货。Turborepo生产调度与流水线系统→ 负责安排哪些车间开工、并行生产、跳过已完成的工序并把成品缓存起来。你当然可以只有物流pnpm或只有调度Turborepo但两者配合工厂效率才能最大化。五、替代方案参考需求替代组合依赖管理yarn workspaces、npm workspaces任务编排Nx、Lerna、Rush一体化方案Nx自带依赖管理 任务编排但pnpm Turborepo因其简洁性、性能、生态兼容性已成为当前前端社区最流行的轻量级 Monorepo 方案。✅ 总结pnpm workspace管依赖安装与链接What to installTurborepo管任务执行与缓存How to run不必强制绑定但组合使用 1 1 2如果你正在搭建 Monorepo直接上 pnpm Turborepo 是最省心、高效的选择 就像 React Vite 一样它们不是技术绑定而是工程体验的最佳拍档。