随着团队规模和项目复杂度的增长,公共 npm registry 已经无法满足所有需求。你可能需要发布仅限公司内部使用的包,或者将多个关联项目放在同一个代码仓库中协同管理。私有 npm 仓库和 Monorepo 方案正是为解决这些问题而生。
20.5.1 为什么需要私有 npm 仓库
直接使用公共 npm registry 存在几个典型的局限:
- 安全性:内部工具库、业务组件如果发布到公网,代码直接暴露。
- 网络稳定性:依赖公共网络,一旦 registry 出现故障或限流,构建全部中断。
- 定制需求:企业需要自定义域名、包命名前缀、访问权限控制。
- 依赖缓存加速:在内网搭建 registry,CI/CD 下载速度可提升数倍。
私有 npm 仓库就是在企业内部搭建的“私有版 registry”,既能代理公共包,又能托管私有包,还能做权限管控。
20.5.2 主流私有 npm 仓库方案
1. Verdaccio
- 轻量、零配置,几行命令即可启动。
- 支持代理公共 npm 仓库,初次请求缓存,后续直接从内部读取。
- 基于 htpasswd 文件或插件实现用户认证与权限。
- 适合中小团队快速上手,维护成本极低。
简易启动示例:
npm install -g verdaccio
verdaccio # 默认启动在 http://localhost:4873
然后设置 npm registry 指向它:npm set registry http://localhost:4873。
2. Nexus Repository / JFrog Artifactory
- 企业级制品仓库,不仅支持 npm,还支持 Maven、Docker、PyPI 等。
- 细粒度的权限管理、存储清理策略、高可用部署。
- 适合大型组织统一管理和审计所有开发制品。
3. GitLab / GitHub Packages
- 直接在代码托管平台启用私有 npm 包功能。
- 与 CI/CD 紧密集成,发布时只需携带 access token。
- 好处是项目与包管理集中一处,无需额外运维服务。
20.5.3 什么是 Monorepo
Monorepo(单一代码仓库)是指将多个项目(如前端应用、后台服务、UI 组件库、工具函数库)放在同一个 Git 仓库中进行管理。与它相对的是 Polyrepo,即每个项目一个独立仓库。
Monorepo 的核心价值:
- 跨项目代码共享与复用:共享的组件或工具库直接通过包引用,不需要单独发包、更新版本。
- 原子化变更:一个功能改动可能涉及多个包,可以在同一个提交中完成修改和测试,不会出现版本不匹配。
- 统一的工具链和规范:ESLint、Prettier、TypeScript 配置共享,CI/CD 流程一致。
20.5.4 主流 Monorepo 工具
1. pnpm workspace(组合式 Monorepo)
pnpm 内置了对 Workspace 的支持,是最轻量的方案。在根目录创建 pnpm-workspace.yaml 定义子包位置:
packages:
- 'packages/*'
- 'apps/*'
然后在不同包中相互引用:
# package.json 中
"dependencies": {
"@myorg/utils": "workspace:*"
}
pnpm 通过软链接将 @myorg/utils 指向对应包的本地路径,安装和更新都极快,且不会重复安装依赖。
实际优势:
- 复用 pnpm 自身的硬链接 + 软链接机制,磁盘占用极小。
- 支持
pnpm run -r对全部包递归执行命令,如统一构建和测试。 - 生态兼容好,Vite、Turborepo 等都支持 pnpm workspace。
2. Turborepo / Lerna + Nx 等智能构建工具
单纯使用 workspace 能共享代码,但当包数量增长,全量构建和测试会变得缓慢。Turborepo、NX 等工具提供了任务缓存与并行调度能力,只有变更的包及其下游依赖会被重新构建,大幅提升 CI 速度。
3. 方案对比
| 方案 | 适用场景 | 优势 | 关注点 |
|------|----------|------|--------|
| pnpm workspace | 简单 Monorepo,包数量 < 30 | 零配置,轻量,速度快 | 需自行管理构建顺序 |
| Turborepo | 需优化构建缓存、增量构建 | 高效缓存,任务编排,支持远程缓存 | 需要学习其 pipeline 配置 |
| Nx | 大型企业级项目 | 功能全面,依赖图可视化,插件生态 | 抽象层较多,学习成本高 |
20.5.5 私有仓库与 Monorepo 结合使用
两者经常配合使用,场景如下:
- 内部共享包发布流程
- 在 Monorepo 中开发通用组件库(如
@company/ui-react)。 - 开发完成后,通过 CI 工作流将该包发布到私有 npm 仓库(Verdaccio 等)。
- 其他不能或不方便放入 Monorepo 的项目(如老旧项目、独立部署的服务),直接从私有仓库安装依赖。
- Monorepo 内复用与外包发布并行
对于一些核心模块,既可以作为 workspace 成员直接被引用,也能通过私有仓库供外部项目使用。只需在包的构建配置中生成产物并执行 npm publish。
- 混合架构
企业可能既有 Monorepo 管理的核心平台,又有独立仓库的扩展服务,它们都通过同一个私有仓库交换依赖,保证版本一致性和安全性。
20.5.6 实践建议
- 包命名规范:采用作用域(scope)包,如
@company-name/package-name,避免与公共包冲突,且方便私有仓库做权限隔离。 - 版本管理:即使 Monorepo 内部使用 workspace 引用,对外发布的私有包仍需严格遵循语义化版本,并配合 Changesets 等工具自动化生成 CHANGELOG。
- 本地开发体验:使用 pnpm workspace 时,改动共享包能立即反映到所有消费它的包中,无需重新链接,这是本地开发最直接的效率提升。
- CI/CD 适配:配置
.npmrc指向私有 registry,并注入只读 token 供安装使用;发布时使用具备写入权限的 token。
通过私有 npm 仓库保障资产安全与访问效率,再用 Monorepo 架构提升多包协作的开发体验,这两项工程化实践已经成为现代 JavaScript 团队的标准配置。它们共同构建了一个高效、可控的内部依赖生态,让“共享代码”不再是个麻烦事。