Monorepo架构可以把多个独立的系统放到一起联调,本文记录基于pnpm > workspace
功能,如何构建将vitepress
和组件库进行联调,并且使用turbo
进行任务顺序编排。
技术栈清单: pnpm
、vitepress
、turbo
一、需求分析
1、最终目标
我们最终需要实现这样的一个Monorepo项目架构:
--project--package.json--packages--docs--package.json--project-one--package.json--pnpm-workspace.yaml
其中各个目录功能如下:
project
:根目录
packages
:子项目包
packages/docs
:基于vitepress的文档项目
packages/project-one
:组件库
2、Monorepo技术选型
实现Monorepo架构的方式主要有以下两种:
-
lerna+yarn
-
pnpm中workspace配置
这里选择后者,因为基于pnpm
的包管理逻辑使用软硬链接,并且重复使用本地依赖包缓存,可以大大提升安装效率,节省依赖包存储空间。而且pnpm
还继承了npm
的所有功能,并且支持node
版本管理,这里非常推荐pnpm
。
3、为什么使用vitepress?
之前有使用过vuepress
,后来vite
伴随vue3
一起被推出,体验后果断切换到vite
,基于esmodule
的调试模式,调试效率直线提升。所以同理,这里也同样推荐使用vitepress
,而不是vuepress
。
4、turbo的作用
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就可以更快的执行构建,大大提高了整体的效率。
二、开发
1、文件结构
首先创建如下文件结构:
--project--package.json--packages--docs--package.json--project-one--package.json--pnpm-workspace.yaml--turbo.json
创建完project
根目录、packages/docs
、packages/project-one
后,分别执行pnpm init
做项目初始化,生成package.json
文件。
并且在跟目录创建 pnpm-workspace.yaml
文件:
packages:- 'apps/*'- 'packages/*'
识别packages
和apps
目录下的项目,作为子项目。这样在执行pnpm add XXX --workspace
时,就会到子项目中去寻找。
2、turbo.json
{"$schema": "https://turbo.build/schema.json","pipeline": {"build": {"dependsOn": ["^build"],"outputs": ["dist/**"]},"lint": {},"dev": {"cache": false,"persistent": true},"clean": {"cache": false}}
}
3、根目录script命令行代理
#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\""
}
4、初始化project-one项目
代码如下:
#index.js
const addOne = (x = 0, msg = '未填') => {console.log('调用project-one的项目是:', msg);return x + 1;
}
export default addOne#index2.js
console.log('nihao index2');
5、初始化docs项目
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: VitePresstext: Vite & Vue powered static site generator.tagline: Lorem ipsum...actions:- theme: brandtext: Get Startedlink: https://github.com/jkkdeng- theme: alttext: View on GitHublink: https://github.com/jkkdengfeatures:- icon: 🛠️title: Simple and minimal, alwaysdetails: Lorem ipsum...- icon: 🛠️title: Simple and minimal, alwaysdetails: Lorem ipsum...- icon: 🛠️title: Simple and minimal, alwaysdetails: Lorem ipsum...
---
<script setup>
import addOne from 'project-one'
addOne(10,'docs')
</script>
import addOne from 'project-one'
,这行会在workspace
中寻找project-one
项目下的package.json
文件,识别"main": "index.js",
配置,然后获取到export
的addOne
方法。最终打印到浏览器控制台。
6、执行
到这里就基本都配置完成了,这里我们把项目跑起来,看下运行效果。
我们打开终端命令行,并进入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
任务。可以看到执行顺序依次是:
- project-one -> build
- project-one -> test
- docs -> build
- docs -> test
-
执行
pnpm run one:dev
-
执行
pnpm run docs:dev
源码