基础骨架:30 分钟搭好 pnpm workspace,完成双项目 Monorepo 迁入

开篇:为什么我们要做 Monorepo

做企业级项目的同学大概率都遇到过这些痛点:

  • 后台管理系统 + 可视化大屏拆分成两个仓库,公共工具、类型定义、请求封装来回复制粘贴,改一处要同步好几个项目;
  • 两个项目依赖版本不统一,同一个 ElementPlus 版本不一样,样式、API 经常出现异常;
  • 本地开发要开两个终端、跑两次 dev 命令,切换项目麻烦,构建配置重复维护;
  • 新手要拉好几个仓库,环境配置一套一套配,上手成本极高。

这就是典型的多仓(Multirepo)维护困境。而 pnpm + Monorepo 就是目前前端行业解决这类问题的最优解:一个仓库管理多个项目,共享依赖、共享公共代码、统一工程规范。

本系列我们就以「Vue3 人事管理后台 + 商用可视化大屏」双业务项目为载体,从零落地一套企业级可复用的 Monorepo 架构,全程贴合真实公司开发规范,学完可以直接套用到公司项目里。

本篇是系列第一篇,我们用 30 分钟搭好最核心的基础骨架,完成双项目迁入。


一、前置环境准备

工具 版本要求 说明
Node.js ≥ 18.x 推荐 LTS 版本,Vite5、Vue3.5 都要求 Node18+
pnpm ≥ 8.x 本系列全程使用 pnpm,性能和 workspace 支持都是最优

检查环境命令:

bash 复制代码
node -v # 查看当前系统已安装的 node 版本
pnpm -v # 查看当前系统已安装的 pnpm 版本

如果没装 pnpm,执行全局安装:

bash 复制代码
npm install -g pnpm

二、第一步:初始化 Monorepo 根项目

Monorepo 的核心是 「一个根仓库 + 多个子项目」,所有全局配置、依赖管理、脚本命令都统一在根目录。

  1. 新建项目根文件夹,命名为 hrms-monorepo,并进入目录:
bash 复制代码
mkdir hrms-monorepo
cd hrms-monorepo
  1. 初始化根目录的 package.json:
bash 复制代码
pnpm init

# 或执行兜底方式(任何版本都能用)
echo '{"name":"项目名称","version":"1.0.0","type":"module"}' > package.json

📢 注意:

pnpm v10 版本对 init 命令做了参数重构,删除了 npm/yarn 那种 init -y 自动确认逻辑 ,不再支持 -y/--yes 参数实现自动确认初始化。

  1. 修改根目录 package.json,补充基础信息和关键配置:
json 复制代码
{
  "name": "hrms-monorepo",
  "version": "1.0.0",
  "description": "Vue3人事后台 + 商用大屏 Monorepo 企业级项目",
  "private": true,
  "type": "module",
  "author": "咖啡无伴侣",
  "license": "MIT"
}

关键说明:

  • private: true 必须加,Monorepo 根项目是私有管理的,不会发布到 npm;
  • type:module 统一使用 ES Module 规范,和 Vite 项目保持一致。

三、第二步:配置 Workspace 工作区(核心)

pnpm Monorepo 靠 pnpm-workspace.yaml 来识别哪些目录是工作区子项目,这是整个框架的核心配置文件。

在根目录新建 pnpm-workspace.yaml

yaml 复制代码
# pnpm 工作区配置
packages:
  # 业务项目:所有可独立运行的应用放这里
  - 'apps/*'
  # 公共包:所有可复用的公共模块放这里,后续逐步新增
  - 'packages/*'

目录分层设计(企业级标准规范)

  • apps/: 存放独立运行的业务应用,每个都是完整可打包的项目

    • apps/hrms-admin:人事管理后台系统
    • apps/hrms-screen:商用可视化大屏
  • packages/ 存放可复用的公共依赖包,给所有 apps 项目引用

    • 后续逐步新增:@hrms/shared 公共工具、@hrms/types 公共 TS 类型、@hrms/request 统一请求封装、@hrms/components 公共组件

这种「业务层 + 公共层」的分层结构,即保证了业务项目的独立性,又实现了公共代码的统一复用。


四、第三步:新建两个业务子项目

把我们之前的两个 Vue 项目,创建在 apps 目录下。

  1. 根目录新建 apps 文件夹:
bash 复制代码
mkdir apps
  1. 创建两个项目,目录结构如下:
plaintext 复制代码
hrms-monorepo/ 
├── apps/
│ ├── hrms-admin/ # 人事后台项目(完整的Vue3+Vite工程) 
│ │ ├── src/
│ │ ├── package.json
│ │ └── vite.config.ts 
│ └── hrms-screen/ # 可视化大屏项目(完整的Vue3+Vite工程) 
│ ├── src/ 
│ ├── package.json 
│ └── vite.config.ts 
├── package.json 
└── pnpm-workspace.yaml

📢 关键注意事项

  1. 子项目的 package.json 里的 name 字段建议改成带作用域的格式,方便后续内部引用:

    • hrms-admin 的 name 改为 @hrms/admin
    • hrms-screen 的 name 改为 @hrms/screen

    示例(apps/hrms-admin/package.json):

    json 复制代码
    {
        "name":"@hrms/admin",
        "version":"1.0.0",
        "private":true,
        "type":"module",
        "scripts":{
            "dev":"vite",
            "build":"vue-tsc && vite build",
            "preview":"vite preview"
        }
        // ... 其他依赖配置保留不变
    }
  2. 删除两个子项目的 .vscodeREADME.md 文件;两个子项目里如果有 .npmrc.gitignore 可以删除了,后续统一在根目录管理,避免配置分散。


五、第四步:根目录统一工程化配置

Monorepo 的一大优势就是全局统一规范,所有配置只维护一份,所有子项目自动生效。

1. 根目录新建 .npmrc(统一包管理器规则)

和我们之前单项目的配置完全一致,全局生效,所有人、所有子项目依赖行为完全统一:

ini 复制代码
# 国内镜像源
registry=https://registry.npmmirror.com
disturl=https://npmmirror.com/dist
sass_binary-site=https://npmmirror.com/mirrors/node-sass
electron_mirror=https://npmmirror.com/mirrors/electron

# 依赖版本严格锁定
save-exact=true
save-prefix=~

# 强制校验 Node 版本、包管理器
engine-strict=true
package-manager-strict=true

# 关闭无用提示
fund=false
audit=false
update-notifier=false

# pnpm 依赖提升兼容第三方库
public-hoist-pattern[]=*element-plus*
public-hoist-pattern[]=*vue*
public-hoist-pattern[]=*echarts*

2. 根目录新建 .gitignore(统一忽略文件)

所有子项目的 node_modules、dist、环境变量都统一在这里忽略:

plaintext 复制代码
# 依赖目录 
node_modules 
.pnpm-store 

# 打包产物 
dist 
dist-ssr 
*.local 

# 编辑器 
.vscode/* 
!.vscode/extensions.json 
!.vscode/settings.json 
.idea 

# 系统文件 
.DS_Store 
Thumbs.db 

# 日志 
*.log 
npm-debug.log* 
pnpm-debug.log*

3. 根目录统一脚本命令

在根目录 package.json 里添加全局脚本,实现「一条命令管理所有项目」

json 复制代码
{
    "scripts":{
        # 全局安装所有依赖
        "install:all":"pnpm install",
        
        # 分别启动项目
        "dev:admin":"pnpm --filter @hrms/admin dev",
        "dev:screen":"pnpm --filter @hrms/screen dev",
        
        # 分别打包项目
        "build:admin":"pnpm --filter @hrms/admin build",
        "build:screen":"pnpm --filter @hrms/screen build",
        
        # 一键打包所有项目
        "build:all":"pnpm -r build"
    }
}

核心知识点:--filter 是 pnpm 的筛选指令,指定只对某个工作区项目执行命令,是 Monorepo 多项目管理的核心语法。-r 是递归执行所有子项目。


六、第五步:安装依赖 & 验证效果

所有配置完成后,在根目录执行一次安装命令:

bash 复制代码
pnpm install

验证是否成功

  1. 看根目录是否只生成一份 node_modules,所有依赖都提升到根目录统一管理,子项目里不会再有冗余的 node_modules;

  2. 执行启动后台项目命令,看是否能正常运行:

bash 复制代码
pnpm dev:admin
  1. 执行启动大屏项目命令,验证双项目都能正常启动:
bash 复制代码
pnpm dev:screen

如果两个项目都能正常启动、正常访问,说明 Monorepo 基础骨架搭建完成。


本篇小结

本篇我们完成了 Nonorepo 最核心的基础搭建:

  1. ✅ 统一环境规范,确定 pnpm + Node18 + 的基础要求
  2. ✅ 配置 pnpm workspace,完成 「apps 业务层 + packages 公共层」的分层架构
  3. ✅ 完成人事后台、可视化大屏双项目迁入
  4. ✅ 根目录统一 .npmrc.gitignore 工程化配置
  5. ✅ 全局脚本统一管理,一条命令启动 / 打包任意项目

到这里,我们已经解决了「多项目依赖不统一、启动维护麻烦」的基础问题。但这只是开始,Monorepo 真正的价值 ------------公共代码复用、统一类型定义、跨项目共享组件,我们会在后面的篇章逐步落地。


【避坑指南】pnpm install 报错 ERR_PNPM_UNSUPPORTED_ENGINE 版本不兼容

触发场景

Monorepo 根目录首次执行 pnpm install 安装依赖时触发,使用 Vite 新版官方模板创建的子项目大概率会遇到。

完整报错信息

plaintext 复制代码
Scope: all 3 workspace projects 
/Users/xxx/Documents/hrms-monorepo/apps/hrms-admin:  ERR_PNPM_UNSUPPORTED_ENGINE  Unsupported environment (bad pnpm and/or Node.js version) 

This error happened while installing a direct dependency of /Users/xxx/Documents/hrms-monorepo/apps/hrms-admin 

Your Node version is incompatible with "npm-run-all2@9.0.2". 

Expected version: ^22.22.2 || ^24.15.0 || >=26.0.0 
Got: v20.20.0 

This is happening because the package's manifest has an engines.node field specified. To fix this issue, install the required Node version. 
Progress: resolved 1, reused 0, downloaded 0, added 0

报错原因

  1. 我们在 .npmrc 中开启了 engine-strict=true 严格引擎校验,pnpm 会检查所有依赖包的 Node 版本要求,不匹配直接中断安装,而非仅输出警告。
  2. 新版 Vite 官方默认依赖的 npm-run-all@9.x 抬高了 Node 版本门槛,要求 Node ≥22.22.2,和当前企业主流的 Node 18/20 LTS 版本不兼容。

两种解决方案

方案一:关闭严格校验(个人学习/Demo 项目首选)

最简单快速的处理方式,版本不匹配仅警告不中断安装,完全不影响正常开发使用。

修改根目录.npmrc

ini 复制代码
# 将 true 改为 false
engine-strict=false

修改完成后重新执行 pnpm install 即可。

方案二:锁定依赖版本(企业级项目推荐,保留严格规范)

通过 pnpm 的 overrides 能力,强制全仓库使用兼容 Node 20 的低版本依赖,保留工程化严格校验规则。

在根目录 package.json 中新增配置:

json 复制代码
{
    "pnpm":{
        "overrides":{
            "npm-run-all2":"^8.0.0"
        }
    }
}

修改完成后重新执行 pnpm install 即可。


💡 面试高配考点

第一篇(Monorepo 基础搭建)

  1. 说一下 Monorepo 和多仓模式的区别,各自有什么优缺点?
  2. pnpm workspace 是怎么管理依赖的,和 lerna 相比优势在哪?
  3. 你们公司为什么要做 Monorepo 改造,解决了什么实际问题?

下一篇预告

第02篇:公共包初体验 ------ 抽取统一 TS 类型定义包,实现双项目共享,告别重复写 interface。

相关推荐
用户1733598075371 小时前
Vue 3 SPA 首屏优化:从 3s 到 1.2s 的 5 个实践
前端·vue.js
谷无姜1 小时前
Webpack5 进阶思考:那些官方文档没讲清楚的事
前端·webpack
weedsfly1 小时前
还在用 Axios?你可能需要重新理解 XHR 与 Fetch
前端·javascript·面试
CoderWeen1 小时前
从零实现一个 Vue3 流程图编辑器:节点拖拽、贝塞尔连线与框选
前端·javascript
森鹿1 小时前
express中间件原理以及大致实现
前端·express
光影少年1 小时前
HashRouter 和 BrowserRouter 区别、底层原理、部署差异
前端·react.js·nestjs
柯克七七1 小时前
我把祖传项目的构建时间砍了90%,领导以为我只是在"优化了一下",结果隔壁组的CI都崩了来问我配置
前端·webpack
风骏时光牛马1 小时前
JSP页面直接输出实体对象空属性引发页面500报错实战案例
前端
IT_陈寒2 小时前
Python里这个赋值坑,连老司机都能翻车
前端·人工智能·后端