构建 monorepo 有很多方式,比较流行的有 nx, yarn, pnpm。nx 主要用于 angular, 同时也支持 react 和 nodejs, 适合构建大型项目,提供一系列好用的样板代码生成工具,但是 nx 所支持的项目有限,并不是很通用,比如像 vue, 就需要额外安装社区提供的插件。还有像 yarn 和 pnpm 提供 workspace,可以满足基本的需求,但是对于 monorepo 项目内的依赖需要手动 link, CI/CD 也支持地不够完善,毕竟他们的定位是包管理工具,不是 build 工具, 但是好处也很明显,简单,上手容易,在可遇见的将来不用担心会没人维护,结合 gulp 使用的话也是不错的选择。
除了上面几种方式,个人比较喜欢和推荐的是微软开发的 rushjs 工具,相比于 yarn 和 pnpm,功能要完善许多,而且是微软开发管理 sharepoint 的,稳定性也应该是有所保障的。
下面介绍通过 rushjs 创建一个 monorepo。

1. 安装并初始化
安装比较简单,直接全局安装:
npm install -g @microsoft/rushmkdir rushapp
rush 提供 init 命令初始化项目:
cd rushapp && rush init
初始化完成之后会出现一个 rush.json 配置文件和一个 common 目录。
2. 创建项目
创建一个项目目录文件 apps:
mkdir apps && cd apps
创建一个 vue 项目:
yarn create vite vueapp --template vue
在 rush.json 的配置项文件里添加 vueapp 项目:
"projects": [{"packageName": "vueapp","projectFolder": "apps/vueapp","reviewCategory": "production"}]
然后执行
rush update
rush 会通过默认的 pnpm 包管理器安装依赖,所有的依赖会安装在总项目目录下的 common 的 temp 文件夹下,而对应的子项目例如 vueapp 目录下同时会 出现 node_modules 目录,而里面的包都是软链接自最外层的 common 下的 temp 目录。
在单个子项目中可以独立执行项目中的各种命令,安装独立项目来操作, 例如:
npm run buildnpm run dev
也可以使用 rushx:
rushx buildrushx dev
3. 添加更多项目
在 apps 目录下添加其他项目, 这次是手动添加:
mkdir server && cd server
初始化项目:
npm init
在在最外层 rush.json 添加项目信息:
"projects": [{"packageName": "vueapp","projectFolder": "apps/vueapp","reviewCategory": "production"},{"packageName": "server","projectFolder": "apps/server","reviewCategory": "production"}]
切换到 server 子项目目录,添加项目依赖:
rush add --package typescript --devrush add --package @types/node --devrush add --package fastify
添加 server.ts 文件:
import fastify from 'fastify'const server = fastify()server.get('/', async (request, reply) => {return 'hello, world.';})server.listen(8080, (err, address) => {if (err) {console.error(err)process.exit(1)}console.log(`Server listening at ${address}`)})
并且配置好 tsconfig.json :
{"name": "server","version": "1.0.0","description": "","main": "index.js","scripts": {"start": "node index.js","build": "tsc -p tsconfig.json","test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","dependencies": {"typescript": "~4.4.3","fastify": "~3.21.0","@types/node": "~16.9.1"}}
rush 的比较好的功能是提供了批量操作的功能,比如执行:
rush build
他会执行全部子项目的 build 命令:
Found configuration in Users/TANGW197/apps/playground/rushapp/rush.jsonRush Multi-Project Build Tool 5.52.0 - https://rushjs.ioNode.js version is 14.17.5 (LTS)Found configuration in Users/TANGW197/apps/playground/rushapp/rush.jsonStarting "rush build"Executing a maximum of 12 simultaneous processes...==[ server ]=======================================================[ 1 of 2 ]=="server" completed successfully in 1.40 seconds.==[ vueapp ]=======================================================[ 2 of 2 ]=="vueapp" completed successfully in 3.09 seconds.==[ SUCCESS: 2 projects ]======================================================These projects completed successfully:server 1.40 secondsvueapp 3.09 secondsrush build (3.10 seconds)
因此 rush 对于自动化 CI/CD 比较友好, build 命令也提供其他参数来单独 build 指定的项目。
4. 创建自引用的库
当前创建的子项目都是相互独立,互不干涉的,但是有的时候需要创建一个 library, 而其他的项目恰好需要引入这个 library, 这时需要用到 link
在 rushapp 总目录与 apps 同级的目录下创建一个 libraries 目录,在这个 libraries 下创建一个 helpers 库。
{"name": "@rushapp/helpers","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC"}
为了不和 npm 线上仓库冲突,特意加上了命名空间作为区分,在 rush.json 文件里配置项目:
"projects": [{"packageName": "vueapp","projectFolder": "apps/vueapp","reviewCategory": "production"},{"packageName": "server","projectFolder": "apps/server","reviewCategory": "production"},{"packageName": "@rushapp/helpers","projectFolder": "libraries/helpers","reviewCategory": "production"}]
我们在 helpers 库中创建一个最简单的函数并导出:
function hello() {return "hello, world!";}export default hello;
假如我们需要在 server 子项目中引入 helpers 库,只需要在 server 的 package.json 添加对应依赖即可:
{"name": "server","version": "1.0.0","description": "","main": "index.js","scripts": {"start": "node index.js","build": "tsc -p tsconfig.json","test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","dependencies": {"typescript": "~4.4.3","fastify": "~3.21.0","@types/node": "~16.9.1","@rushapp/helpers": "~1.0.0"}}
然后执行 rush update
, 在 server 代码里稍作更改:
import fastify from 'fastify'import helpers from '@rushapp/helpers';const server = fastify()server.get('/', async (request, reply) => {reply.send(helpers());})server.listen(8080, (err, address) => {if (err) {console.error(err)process.exit(1)}console.log(`Server listening at ${address}`)})
5. 部署
执行 rush build
就可以将三个子项目批量编译,编译完之后照理说可以直接将整个项目打包部署,但是这样整个包的体积会非常的大, 最好的方式是通过类似 npm pack 的方式将必要的文件和依赖提取出来,然后再将其打包,部署。
rush 提供了 deploy 命令来实现对应的功能, 生成一个 deploy 配置, 在主目录下执行:
rush init-deploy -p server
会在 common/config/rush
生成一个 deploy.json
文件, 配置完需要打包的参数就可以执行:
rush deploy
进行打包了,但是这里需要注意的是 vueapp 是 dist 编译后的目录文件,但是 deploy.json 的配置没有提供复制文件夹的功能,但是通过命令行也很容易实现:
rush build && rush deploy --overwrite && mv apps/vueapp/dist common/deploy
此时打包出来的文件已经小了不止一个数量级了,只有不到 7M,因此可以直接将 common/deploy 文件夹部署到服务器上。
6. Rush stack

rush 是 rush stack 提供的一个 monorepo 管理工具,除了 rush, rush stack 还提供了其他像 heft build 工具, api 提取,文档生成等工具, 其中 heft 结合 rush, 可以实现更灵活和强大的工作流。




