Monorepo架构可以把多个独立的系统放到一起联调,本文记录基于pnpm.workspace功能,如何构建将vitepress和组件库进行联调,并且使用turbo进行任务编排。
技术栈清单: pnpm 、vitepress 、turbo
我们最终需要实现这样的一个Monorepo项目架构:
--project
--package.json
--packages
--docs
--package.json
--project-one
--package.json
--pnpm-workspace.yaml
其中各个目录功能如下:
project:根目录
packages:子项目包
packages/docs:基于vitepress的文档项目
packages/project-one:组件库
实现Monorepo架构的方式主要有以下两种:
lerna+yarn
pnpm中workspace配置
这里选择后者,因为基于pnpm的包管理逻辑使用软硬链接,并且重复使用本地依赖包缓存,可以大大提升安装效率,节省依赖包存储空间。而且pnpm还继承了npm的所有功能,并且支持node版本管理,这里非常推荐pnpm。
之前有使用过vuepress,后来vite伴随vue3一起被推出,体验后果断切换到vite,基于esmodule的调试模式,调试效率直线提升。所以同理,这里也同样推荐使用vitepress,而不是vuepress。
turbo全名turborepo,本身也是一个Monorepo管理工具。这里主要使用其任务编排能力,能够按正确的顺序执行Monorepo内的项目的任务。
例如:Monorepo里有三个项目A,B,C。A和C还需要依赖于B。也就是说B项目需要先构建好后,A和C才能构建。
正常每个项目都按照这个顺序做任务编排:lint->build->test->deploy。即检查规范->构建项目->测试项目->部署项目。

从上图可以看到,使用lerna时,A、B、Cd这三个项目中的lint、build、deploy都是并行进行的。如果A、C都依赖B,那么A、C在build的时候,很可能 B d还没有build完成,会拖慢A、C的build效率。
但是turbo能够将每个项目里的任务进行拓扑排序再执行。即图中B项目并行执行lint,test,build,之后A和C就可以更快的执行构建,大大提高了整体的效率。
project根目录、packages/docs、packages/project-one三个项目目录,然后分别进入这三个目录执行pnpm init做项目初始化,生成package.json文件。pnpm-workspace.yaml 文件:packages:
- 'apps/*'
- 'packages/*'
识别packages和apps目录下的项目,作为子项目。这样在执行pnpm -F subProject add XXX --workspace安装命令时,就会到子项目中去寻找名为XXX的包。
完成上述操作后,文件结构如下:
--project
--package.json
--packages
--docs
--package.json
--project-one
--package.json
--pnpm-workspace.yaml
--turbo.json
pnpm add turbo -gturbo.json文件,代码如下:{
"$schema": "https://turborepo.org/schema.json",
"pipeline": {
"build": {
"dependsOn": [
"^build"
]
},
"test": {
"dependsOn": [
"build"
],
"outputs": [],
"inputs": [
"src/**/*.tsx",
"src/**/*.ts",
"test/**/*.ts",
"test/**/*.tsx"
]
},
"lint": {
"outputs": []
},
"deploy": {
"dependsOn": [
"build",
"test",
"lint"
],
"outputs": []
}
}
}
配置项说明:
$schema:定义了 json 的类型,类似于 ts 对于 js 的约束dependsOn:表示当前命令所依赖的相关流程pipeline.build:表示当执行 turbo build 时会预先执行 ^build, ^build 就是所有项目的 package.json 里的那个 build 脚本,^ 是一个标记。如果像 pipeline.test 中的 build,他就指的是 pipeline 中的第一个 build 命令。outputs:指定缓存输出目录inputs: 配置的目录下文件有变化,才会重新执行此命令了解更多请参照:turbo官网
#package.json
"scripts": {
"turbo-test": "turbo run test",
"turbo-build": "turbo run build",
"test": "echo \"Error: no test specified\" && exit 1",
"docs:dev": "pnpm --filter \"docs\" dev",
"docs:build": "pnpm --filter \"docs\" build",
"docs:serve": "pnpm --filter \"docs\" serve",
"docs:preview": "pnpm --filter \"docs\" preview",
"one:dev": "pnpm --filter \"project-one\" dev"
}
#docs package.json
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"serve": "vitepress serve",
"preview": "vitepress preview",
"test": "echo \"Error: no test specified\""
}
#project-one package.json
"scripts": {
"dev": "node index2.js",
"build": "node index2.js",
"test": "echo \"Error: no test specified\""
}
代码如下:
#index.js
const addOne = (x = 0, msg = '未填') => {
console.log('调用project-one的项目是:', msg);
return x + 1;
}
export default addOne
#index2.js
console.log('nihao index2');
docs使用vitepress构建,打开终端,进入docs目录,执行下面命令,对docs初始化:
# 安装vitepress
pnpm add -D vitepress
# 初始化项目,这里会出现交互式命令行,默认回车下一步就行
pnpm dlx vitepress init
调整docs/package.json代码如下:
{
"name": "docs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"serve": "vitepress serve",
"preview": "vitepress preview",
"test": "echo \"Error: no test specified\""
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"project-one": "workspace:^",
"vitepress": "1.0.0-rc.20",
"vue": "^3.2.45"
},
"devDependencies": {}
}
创建docs/index.md文件,代码如下:
---
layout: home
hero:
name: VitePress
text: Vite & Vue powered static site generator.
tagline: Lorem ipsum...
actions:
- theme: brand
text: Get Started
link: https://github.com/jkkdeng
- theme: alt
text: View on GitHub
link: https://github.com/jkkdeng
features:
- icon: 🛠️
title: Simple and minimal, always
details: Lorem ipsum...
- icon: 🛠️
title: Simple and minimal, always
details: Lorem ipsum...
- icon: 🛠️
title: Simple and minimal, always
details: Lorem ipsum...
---
注:import addOne from 'project-one',这行会在所有子项目中寻找project-one项目,然后识别project-one/package.json下"main": "index.js",配置,然后获取到export的addOne方法。最终打印到浏览器控制台。
到这里就基本都配置完成了,这里我们把项目跑起来,看下运行效果。
我们打开终端命令行,进入project根目录.
执行pnpm run turbo-build
#turbo.json
"build": {
"dependsOn": ["^build"]
},
系统会找到turbo.json中pipeline.build下的配置:"dependsOn": ["^build"],^build含义是指上游的build任务优先执行。猜测turbo应该对各个子包中的package.json进行扫描,查看是否存在依赖关系,例如我们这里是docs项目依赖project-one项目,所有优先对proect-one项目进行build,然后在对docs进行build。看下面的运行结果也的确如此。

执行pnpm run turbo-test
#turbo.json
"test": {
"dependsOn": ["build"]
},
这里的build就是pipeline中的build任务,含义是:每个项目要执行自己的test任务,就要先执行build任务。
可以看到执行顺序依次是:

执行pnpm run one:dev

执行pnpm run docs:dev

