技术没有银弹但好的脚手架能让你少走三年弯路。本文将带你从零理解frontend-monorepo-starter这个项目的设计理念、架构分层、技术选型和上手操作适合任何想搭建企业级前端 Monorepo 的开发者。 项目仓库https://github.com/1648298170/frontend-monorepo-starter文末有详细引导建议先 Star 收藏方便随时回看一、这个项目是什么为什么值得看一句话概括这是一个面向真实业务、长期维护而设计的前端 Monorepo 模板starter / 脚手架。市面上很多 Monorepo 教程只教你怎么把多个项目塞进一个仓库“多包同仓”但实际企业开发中真正头疼的是这些问题❌ 新增一个应用要重新配一遍 ESLint、TypeScript、Vite……配置总是对不齐❌ React 和 Vue 团队共用一段逻辑互相 copy改一处忘改另一处❌ 依赖版本各搞各的React 18 和 19 混用莫名报错❌ 项目越来越大构建越来越慢不知道哪里可以复用、哪里该拆分❌ 新人来了不知道代码该往哪个目录放约定全靠口头传授frontend-monorepo-starter用一套清晰的分层 自动化工具链把上面这些问题一次性解决。它不是炫技项目而是可以直接拿来开新业务的实战模板。它的核心能力一览能力实现多应用共存同一仓库内同时管理 React 应用和 Vue 应用跨框架共享React/Vue 共用框架无关逻辑工具函数、请求、权限、配置统一工程规范一套 ESLint / TypeScript / Prettier / Stylelint 配置全仓库复用依赖版本统一pnpm catalog 集中管理所有生态依赖版本一键代码生成内置非交互式生成器命令行生成应用/组件/页面/Store质量保障单元测试 Vitest 端到端 Playwright 死代码检测 Knip高效构建Turborepo 任务编排 缓存Vite 8 构建二、核心理念把分层刻进骨头里理解这个项目只需要理解一个核心思想分层 依赖方向。1. 四个代码区域各司其职项目把所有代码划分成四个区域每个区域职责单一apps/ ← 应用层薄组装路由、页面、布局 packages/ ← 共享层厚沉淀可复用能力 shared/ ← 框架无关React/Vue 都能用 react/ ← React 专属 vue/ ← Vue 专属 tooling/ ← 工程配置ESLint/TS/Vite scripts/ ← 仓库级脚本生成器、校验、版本管理 docs/ ← 架构文档、开发规范用大白话解释apps/里的应用只负责组装不沉淀业务逻辑。就像搭乐高应用是拼好的成品但每个零件都来自packages/。packages/shared/放最纯粹的逻辑——比如格式化日期、发请求、判断权限。它们不认识 React也不认识 Vue所以两边都能用。packages/react/和packages/vue/放和框架绑定的能力——比如 React 的 UI 组件、Vue 的 composables。packages/tooling/放工程配置——全仓库共享的 ESLint 规则、tsconfig改一处全局生效。2. 严格的依赖方向这是整个架构的灵魂约束app → pages → features → app shared → packages翻译成人话就是依赖只能向外不能向内绕✅ 应用可以用 packages 里的能力✅ React 应用可以用 shared 里的工具函数❌ React 应用不能用 Vue 包里的东西反之亦然❌ shared 包不能依赖 React 或 Vue否则就没法跨框架复用❌ 应用 A不能直接 import 应用 B 的代码这些规则不是写在文档里靠自觉而是由 ESLint 的repo/eslint-config/boundaries规则自动检查你违规了lint 直接报错根本提交不了代码。3. 包的公开 API约定每个 workspace 包都通过src/index.ts暴露对外接口并且package.json的exports字段锁死入口。// ✅ 推荐通过包名导入import{formatDate}fromrepo/utils;// ❌ 禁止深层路径导入包内部结构调整会直接破坏调用方import{formatDate}fromrepo/utils/src/date;这个约定看着简单却是 Monorepo长期可维护的关键包内部怎么重构都行只要index.ts的导出不变调用方完全无感知。三、技术栈全景图这个项目的技术选型非常克制且前沿每个选择都有明确理由分类技术为什么选它包管理pnpm 10 workspace硬链接节省磁盘workspace 原生支持 monorepo依赖版本治理pnpm catalog一个文件统一所有依赖版本告别版本混乱任务编排Turborepo智能缓存 并行构建build自动按依赖拓扑排序构建工具Vite 8 Rolldown极速冷启动和 HMRRolldown 是新一代打包器React 栈React 19 React Router Zustand最新的 React 极简状态管理无 Provider 模板Vue 栈Vue 3 Vue Router Pinia官方推荐全家桶样式Tailwind CSS 4 Sass Design Token原子化 CSS 主题变量 规范化检查代码质量TypeScript 6 ESLint Prettier类型安全 代码风格统一测试Vitest单元 PlaywrightE2EVite 原生测试 真实浏览器端到端测试死代码检测Knip自动找出没被引用的导出保持仓库干净重点说一下 pnpm catalog依赖目录这是 pnpm 比较新的特性也是这个项目的依赖治理杀手锏。传统痛点React、Vue、各种插件分散在每个应用的package.json里升级 React 要改十几个文件还容易漏。catalog 方案所有生态依赖的版本统一写在pnpm-workspace.yamlcatalog:react:^19.2.1vue:^3.5.25zustand:^5.0.9各应用在自己的package.json里只写react: catalog:实际版本由 catalog 决定。升级 React只改 catalog 一处然后pnpm install全仓库同步。这就是单一数据源的威力。注意catalog只统一版本不改变依赖归属。哪个应用用 axios还得在自己 package.json 声明不会因为 catalog 登记了就全局安装。四、共享能力层详解packages/shared这是项目最精华的部分。packages/shared下有 5 个框架无关的包它们是 React 和 Vue 应用共享的地基1.repo/utils— 纯工具函数最简单的包比如日期格式化exportfunctionformatDate(value:Date|string|number,localezh-CN){constdatevalueinstanceofDate?value:newDate(value);returnnewIntl.DateTimeFormat(locale,{year:numeric,month:2-digit,day:2-digit,}).format(date);}不依赖任何框架React 和 Vue 直接import { formatDate } from repo/utils就能用。2.repo/request— 框架无关请求客户端这是个设计得非常克制的请求封装。核心是createRequestClient工厂函数constclientcreateRequestClient({baseUrl:/api,timeoutMs:30000,getToken:()authService.getToken(),// 应用注入鉴权onRequest:(ctx){/* 请求拦截 */},onResponse:(res,ctx){/* 响应拦截 */},onError:(err,ctx){/* 错误监控 */},});设计亮点基于浏览器原生fetch不绑定 axios/ofetch超时控制用AbortController自动清理定时器避免内存泄漏业务取消 超时取消双信号合并AbortSignal.any错误归一化非 2xx 转成统一的HttpError超时转成RequestTimeoutError可测试测试时注入 mock fetch无需真实网络这套设计让发请求这件事完全脱离框架React 和 Vue 用同一套客户端各自只管注入鉴权策略。3.repo/auth— 权限判断核心函数hasPermission纯逻辑判断不依赖状态库exportfunctionhasPermission(grantedPermissions,// 用户已拥有的权限requiredPermissions,// 目标需要的权限modeall// all 全部满足 | any 满足任一){if(requiredPermissions.length0)returntrue;// 无要求公开// ...用 Set 做 O(1) 查找}应用层React 的 Provider 或 Vue 的路由守卫只要传入权限集合复用同一套判断逻辑。4.repo/config— 运行时配置解析createAppConfig(env)负责把 Vite 注入的环境变量解析成结构化的配置对象并做校验exportfunctioncreateAppConfig(env:RuntimeEnv):AppConfig{constappNamereadNonEmptyString(VITE_APP_NAME,env.VITE_APP_NAME,Frontend App);constapiBaseUrlreadNonEmptyString(VITE_API_BASE_URL,env.VITE_API_BASE_URL,/api);validateApiBaseUrl(apiBaseUrl);// 必须是绝对 URL 或以 / 开头return{appName,apiBaseUrl,environment:normalizeEnvironment(...)};}配置错误在启动时就抛出而不是运行到一半才崩。5.repo/constants— 共享常量放业务无关的常量比如环境标识、枚举值。框架适配层packages/react、packages/vueshared里的能力是裸的框架适配层负责把它们翻译成 React/Vue 能直接用的形态shared 包React 适配Vue 适配authrepo/react-authProvider/Hookrepo/vue-authcomposable/directiveutils/uirepo/react-ui组件repo/vue-ui组件五、应用内部结构appsReact 和 Vue 应用采用相同的业务分层概念但各自遵循框架原生用法src/ app/ ← 应用装配层只组装不写业务 App.tsx/App.vue router/ ← 路由模块化 store/ ← 应用级状态侧边栏、主题等 runtime/ ← 启动时创建的服务单例 pages/ ← 路由页面组合 feature处理路由参数 features/ ← 业务能力登录、订单、权限... feature/ components/ hooks/ 或 composables/ model/ api/ hooks/ ← React 应用级 Hook composables/ ← Vue 应用级 Composable shared/ ← 应用内共享非 Hook/Composable styles/状态管理的分层哲学这是很多团队踩坑的地方。项目明确规定了什么状态该放哪状态类型放在哪里例子组件局部状态框架原生状态useState/ref弹窗输入、按钮 loadingFeature 状态features/feature/store/编辑器草稿、多步骤流程应用级状态app/store/侧边栏、主题、通知中心URL 可分享状态写进路由 URL别进 Store筛选、分页、排序、当前 Tab服务端数据请求缓存方案别复制到 Store列表数据、详情关键原则不是所有状态都要进全局 Store能用 URL 表达的就用 URL刷新可恢复、可分享表单输入留在组件里。Store 只放跨组件跨页面且无法放进 URL的状态。路由模块化路由不是一坨写在一个文件里而是按业务域拆分app/router/ index.ts ← 只负责组合 routes/ home.routes.ts account.routes.ts system.routes.ts页面默认动态导入懒加载路由文件不执行 API 请求、不在顶层读 Store——保持路由文件的纯粹。六、代码生成器告别手搓模板文件这是提升开发效率的利器。项目内置了非交互式代码生成器pnpmg--help# 查看帮助g 是 generate 的缩写支持生成的类型类型作用app生成完整 React/Vue 业务应用component生成组件应用级/Feature/共享feature生成业务 Feature 入口page生成页面不自动改路由storeReact 生成 ZustandVue 生成 Piniahook/composable生成 React Hook / Vue Composable实战示例生成一个新应用pnpmg app\--nameadmin-web\--frameworkreact\--display-name运营管理后台生成 Feature 内的组件pnpmg component--appreact-web--featureorder--nameorder-list生成器的设计原则非常严谨️生成前展示完整文件计划让你看清要生成什么️已有文件一律拒绝覆盖不会误删你的代码️默认生成单元测试--skip-test可关闭️支持--dry-run只校验不写盘️页面生成不自动改路由——避免工具瞎猜权限和布局️未知参数直接报错——防止拼写错误被静默忽略这种非交互式 严格校验的设计让它既适合本地开发也适合接入 CI 脚本自动化调用。七、快速上手从零启动项目1. 环境准备工具版本要求Node.js22.12.0pnpm10.18.3唯一允许的包管理器推荐启用 Corepack 自动管理 pnpm 版本。2. 安装依赖pnpminstall⚠️必须用 pnpm禁用 npm/yarn。项目有preinstall脚本自动校验运行时版本。3. 启动应用pnpmdev:react# 启动 React 应用 → http://localhost:5174pnpmdev:vue# 启动 Vue 应用 → http://localhost:5173pnpmdev# 同时启动所有应用端口由各应用的.env管理不写死在 scripts 里。端口冲突就改.env.localDEV_SERVER_PORT51804. 开发代理配置本地联调后端在.env配置DEV_PROXY_PREFIX/api DEV_PROXY_TARGEThttp://localhost:3000Vite 会自动把/api/xxx转发到后端并移除/api前缀。配置错误前缀不以/开头、target 不是合法 URL会在启动时直接报错。八、提交前必做的质量检查项目提供了一套完整的验证命令矩阵pnpmformat:check# 格式检查pnpmtypecheck# 类型检查pnpmlint# ESLint 检查含依赖边界pnpmtest# 单元测试pnpmbuild# 构建pnpmlint:unused# 死代码检测Knippnpmverify:app-templates# 验证应用模板重型CI 用pnpmtest:e2e# 端到端测试这些命令背后是Turborepo 的智能编排build会自动先构建它依赖的包dependsOn: [^build]构建结果会被缓存没改动的包直接用缓存秒级完成typecheck和test同样依赖构建产物ESLint 边界检查实战前面说的依赖方向约束具体是这样工作的。ESLint 配置会自动扫描 apps 目录根据每个应用 package.json 的依赖判断它是 React 还是 Vue 应用然后套用对应规则和边界约束。你如果在一个 Vue 应用里import了 React 包或者在 shared 包里 import 了 Vuelint 会立刻报错。架构约束被自动化了不靠人肉 review。九、这个项目的设计哲学总结透析下来这个项目有几条贯穿始终的哲学非常值得学习1. 约定优于配置Convention over Configuration目录怎么分层、代码放哪、包怎么导出——全部有明确约定。新人来了不用猜照着约定走就行。2. 约束靠工具保证不靠自觉依赖方向、版本统一、代码风格——全都有自动化工具ESLint、catalog、Prettier兜底。人都会犯错但工具不会。3. 薄应用厚共享应用只管组装能力沉淀到 packages。新增应用时复用现成能力不用重复造轮子。4. 框架无关优先能用纯逻辑解决的请求、权限、配置、工具绝不绑死框架。这是 React/Vue 共存的根基。5. 显式优于隐式包必须声明自己用的依赖、配置错误立即抛出、未知参数直接报错——宁可启动失败也不要运行时玄学 bug。6. 可测试、可验证共享能力默认带单元测试模板有verify:app-templates重型验证端到端有 Playwright。每一层都能被独立验证。十、适合谁用怎么用✅ 强烈推荐给要开新前端项目的技术负责人直接 clone 改造省掉 80% 的基建时间React Vue 混栈团队它的跨框架共享设计正是为你准备的想学习企业级前端工程化的开发者这是教科书级的 Monorepo 实践受够了自己项目配置混乱的人看看有约束的项目长什么样 快速起步建议先 clone 仓库跑通pnpm dev读docs/guides/project-guide.md项目自带的使用指南写得非常细用pnpm g生成你的第一个 feature感受生成器按业务需要把 shared 包的能力扩展成自己的业务基建十一、进阶实战用 Git Worktree 让多分支并行开发丝滑无比到这里项目本身的架构、理念、用法已经讲透了。最后再送你一个进阶锦囊——把 Git Worktree 和这个 Monorepo 结合能让多分支并行开发效率翻倍。很多团队没用过这个特性但它其实早就内置在 Git 里了。1. 先搞懂什么是 Git Worktree一个痛点场景你在main分支开发新功能写到一半同事说线上有个紧急 bug 要修。你只能git stash暂存当前改动容易忘 stash 了什么 切到hotfix分支修 bug 切回maingit stash pop恢复冲突就头疼了而且Monorepo 项目尤其痛苦切分支后node_modules可能要重新装Vite 缓存失效启动又变慢。Git Worktree 的解法让你同一个仓库在磁盘上同时存在多个工作目录每个目录对应一个分支互不干扰。frontend-monorepo-starter/ ← 主工作区main 分支 apps/ packages/ ... frontend-monorepo-starter-hotfix/ ← worktreehotfix 分支 apps/ packages/ ... frontend-monorepo-starter-feat/ ← worktreefeature 分支 apps/ packages/ ...三个目录三个分支各装各的依赖、各跑各的 dev server切换上下文只是切 IDE 窗口而已。再也不用 stash 来 stash 去2. 和 Monorepo 项目结合为什么是绝配frontend-monorepo-starter这种项目有几个特点正好和 worktree 互补特点传统分支切换的痛点Worktree 的优势依赖体量大切分支后 pnpm 链接结构可能变化每个 worktree 独立node_modules互不影响构建缓存Turborepo切分支缓存频繁失效各 worktree 独立缓存构建快多应用同时跑dev server 端口打架不同 worktree 各跑各的端口跨框架协作React/Vue 改动频繁切换一个 worktree 专心改 React另一个改 Vue用大白话Monorepo 项目重依赖多、构建久切换成本高。Worktree 用空间换时间让每个分支都有自己干净独立的工作台。3. 实战操作5 分钟上手前提先把项目 clone 到本地假设主目录叫frontend-monorepo-starter。第①步创建一个 worktree开新分支# 在主仓库目录下执行gitworktreeadd../frontend-monorepo-starter-hotfix hotfix/login-bug这条命令做了三件事在../frontend-monorepo-starter-hotfix创建一个新目录把hotfix/login-bug分支检出到这个目录这个目录和主仓库共享同一个.git不是完整 clone省空间第②步在新 worktree 里装依赖cd../frontend-monorepo-starter-hotfixpnpminstall# 独立安装不影响主工作区省时技巧pnpm 用的是硬链接 全局 store所以即使是新 worktreepnpm install也很快——实际文件大多是从全局 store 链接过来的不会真的下载。第③步启动开发pnpmdev:react# 在 hotfix worktree 里跑 React 应用现在你同时打开两个 IDE 窗口主目录继续写新功能hotfix 目录专心修 bug互不打架。第④步干完活清理 worktree# 回到主仓库目录cd../frontend-monorepo-starter# 查看当前所有 worktreegitworktree list# 删除用完的 worktree分支还在只是工作目录删了gitworktree remove../frontend-monorepo-starter-hotfix4. 配合本项目的最佳实践基于这个 Monorepo 的结构推荐这套 worktree 使用姿势① 用约定俗成的目录命名一眼区分gitworktreeadd../fms-hotfix hotfix/xxx# 紧急修复gitworktreeadd../fms-feat feature/xxx# 新功能gitworktreeadd../fms-review review/xxx# Code Review 专用② 不同 worktree 用不同端口避免冲突本项目的端口由.env控制给每个 worktree 设不同端口# fms-hotfix/.env.local DEV_SERVER_PORT5181 # fms-feat/.env.local DEV_SERVER_PORT5182这样多个 worktree 的 dev server 可以同时跑端口不打架。③ 依赖目录用 pnpm别用 npm/yarn这点AGENTS.md里反复强调。pnpm 的 store 机制让 worktree 的pnpm install接近秒装换 npm 就要老老实实重新下载worktree 的省时优势就没了。④ 把node_modules和构建产物加进 worktree 的忽略worktree 是普通目录.gitignore仍然生效node_modules、dist、.turbo都不会被 git 跟踪放心用。5. 常见命令速查表# 创建 worktree新分支gitworktreeadd路径-b新分支名# 创建 worktree已有分支gitworktreeadd路径已有分支名# 查看所有 worktreegitworktree list# 删除 worktreegitworktree remove路径# 修剪元数据删除已不存在的 worktree 记录gitworktree prune# 移动 worktreegitworktree move旧路径新路径6. 一句话总结Git Worktree pnpm Monorepo 多分支并行开发的空间换时间最优解。每个 worktree 都是独立的工作台独立依赖、独立缓存、独立端口。你在写新功能的同时随手就能切到另一个 worktree 修紧急 bug再也不用stash来stash去了。 想体验这套丝滑流程先把项目 clone 下来试试https://github.com/1648298170/frontend-monorepo-starter⭐ 写在最后去仓库点个 Star 吧这篇文章只是把这个项目的骨架讲清楚真正的精华在源码和文档里——这个项目的docs/目录写得极其详尽覆盖了架构、规范、CI、教学文档本身就是一份值得收藏的前端工程化学习资料。GitHub 仓库https://github.com/1648298170/frontend-monorepo-starter强烈建议你⭐点个 Star——这是对作者最直接的支持也能方便你以后随时找到Fork 或 Clone——直接作为下一个项目的起点仔细读 docs/——里面的设计文档比很多付费课程都讲得透Watch 仓库——跟进后续更新这个项目还在持续演进好的工程实践值得被更多人看到。如果你在搭建前端 Monorepo 时遇到过配置混乱、复用困难、协作痛苦的问题这个项目会给你一套经过深思熟虑的答案。如果这篇文章对你有帮助欢迎点赞、收藏、转发有问题欢迎在评论区交流也可以去 GitHub 仓库提 Issue。仓库地址再放一次别忘了 Starhttps://github.com/1648298170/frontend-monorepo-starter