1. 为什么我们需要共享模块代码
前端开发走到今天,模块化已经成为标配。但当我们同时维护多个项目时,一个尴尬的问题就出现了:如何在多个项目间优雅地共享公共代码?传统的npm/yarn方案会带来各种困扰,比如重复安装导致的磁盘空间浪费、版本不一致引发的诡异bug、本地调试时繁琐的link操作...
我最近接手的一个企业级后台管理系统就遇到了这个痛点。系统由1个主应用和7个子应用组成,这些应用都依赖相同的工具函数库、UI组件和业务逻辑模块。最初我们简单粗暴地在每个项目里都npm install一遍这些共享模块,结果node_modules文件夹加起来占了近10GB空间,更糟的是某次更新后,不同项目里的工具函数版本出现了差异,导致线上出了严重bug。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. pnpm的共享机制解析
2.1 硬链接与符号链接的黑科技
pnpm的共享能力建立在两项核心技术之上:内容寻址存储和硬链接。当你在多个项目中使用相同版本的依赖时,pnpm不会像npm/yarn那样在每个项目的node_modules里都复制一份文件,而是采用硬链接指向磁盘上的同一份物理文件。
举个例子,假设你有5个项目都用了lodash@4.17.21:
- npm/yarn方案:磁盘上会有5份完全相同的lodash代码
- pnpm方案:磁盘上只有1份lodash代码,5个项目通过硬链接指向它
实测下来,我们的项目组切换pnpm后,磁盘占用从原来的10GB降到了3GB左右,CI构建时的依赖安装时间也缩短了40%。
2.2 幽灵依赖的终结者
pnpm采用严格的node_modules结构,每个项目只能访问自己显式声明的依赖。这种设计彻底解决了"幽灵依赖"问题(即项目能意外访问到依赖的依赖)。虽然初期可能需要调整一些不规范的项目结构,但从长远看大大提高了构建的确定性。
3. 实战:搭建monorepo共享模块
3.1 初始化工作区
首先全局安装pnpm(建议版本≥7.0):
bash复制npm install -g pnpm@latest
创建项目根目录并初始化:
bash复制mkdir my-monorepo && cd my-monorepo
pnpm init
关键步骤是创建pnpm-workspace.yaml文件:
yaml复制packages:
- 'packages/*'
- 'apps/*'
这种结构将代码分为两类:
- packages/ 存放共享模块(组件库、工具函数等)
- apps/ 存放具体应用项目
3.2 创建第一个共享模块
在packages目录下新建工具库:
bash复制mkdir -p packages/utils && cd packages/utils
pnpm init
修改package.json重要字段:
json复制{
"name": "@my-project/utils",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"prepublishOnly": "pnpm run build"
}
}
注意:共享模块的name建议使用@scope/package格式,避免命名冲突
3.3 在应用中使用共享模块
假设我们有个React应用在apps/admin:
bash复制cd apps/admin
pnpm a
