技术选型
使用 Pnpm + Turbo 搭建 Web Component Monorepo项目 stencil-component-ui 组件库
- pnpm 作为包管理器
- Turborepo 作为构建系统
- Vitepress 管理文档
pnpm 技术
什么是 pnpm? 它有哪些优势?
pnpm 跟 npm、yarn一样,都是用于管理Node包依赖的管理器,它是号称新一代的最先进包管理工具。按照官网说法,它相比其他包管理工具,可以大大节约磁盘空间并提升安装速度,创建非扁平化的 node_modules
文件夹,目录结构很清晰,具体介绍可以参考 pnpm 官网
pnpm 提出了 workspace 的概念,内置了对 monorepo 的支持,那么为什么用 pnpm 取代之前的 lerna 呢?
这里总结了以下几点原因:
- lerna 已经不再维护,后续有任何问题社区无法及时响应
- pnpm装包效率更高,并且可以节约更多磁盘空间
- pnpm本身就预置了对monorepo的支持,不需要再额外第三方包的支持
pnpm 搭建 menorepo 工程
在工程根目录下新建 packages
目录,并且在 packages
目录下创建 components
和 icons
两个子项目,这里使用 stencil
脚手架,进入 packages 目录,根据 Stencil 官网 创建项目
pnpm create stencil
有三个选项,直接回车选择第一个 components
是创建组件库项目的,输入项目名称即可创建项目
分别使用 stencil 创建了 components
和 icons
项目,components 是来开发组件库源码的,icons 是用来开发编译 svg 图片和组件的,目录如下
在工程根目录建一个 pnpm-workspace.yaml
,用于启用 workspace :
packages:
- "packages/*"
- "docs"
以上指定工作空间内的包依赖关系,packages
用于管理源码,docs
编写文档,然后执行 pnpm install
安装依赖
由于工程根目录 package.json
不需要发包,需要配置 "private": true
在项目中安装包
Pnpm 启用了 workspace,用 Pnpm 安装依赖必须指定安装的位置。-w
是 --workspace-root
的别名,即安装到工程根目录,作为所有子模块的公共依赖。也可以用 -r
递归给每个子模块安装,或者用 --filter <package_name>
给指定子模块安装。-D
是 --save-dev
的别名,即安装依赖到 devDependencies
节点下,不指定参数默认安装到 dependencies
节点。
给每个项目起个包名,修改components
和 icons
项目 package.json
中的 name
字段为 @swc-ui/components
和 @swc-ui/icons
,docs 使用 vitepress 搭建,包名直接用 docs
这一步比较关键,安装包、构建、发包都需要用到这个包名。@swc-ui 是提前创建好的 scope,如果没有的话需要先创建
图标库、组件库包安装到 docs 使用,图标库包安装到组件库项目中使用,使用 --filter
指定安装包的位置
pnpm add @swc-ui/components @swc-ui/icons --filter=docs
pnpm add @swc-ui/icons --filter=@swc-ui/components
components 安装 @swc-ui/icons
后,package.json
新增了 "@swc-ui/icons": "workspace:^"
"dependencies": {
"@swc-ui/icons": "workspace:^"
}
通过 Pnpm 提供的 Workspace Protocol,可以很方便地实现子模块互相引用。在开发的时候,也推荐使用 workspace:^
,这样可以确保依赖的是最新版本代码。当我们用 pnpm publish
发包的时候,Pnpm 会将 workspace:^
替换为实际的版本。
只允许 pnpm
当在项目中使用 pnpm 时,如果不希望开发者使用 yarn 或者 npm 安装依赖,可以将下面的这个 preinstall 脚本添加到工程根目录下的 package.json
中:
"preinstall": "npx only-allow pnpm"
因为在在 Pnpm workspace 模式下 npm install
或者 yarn install
安装依赖无法兼容,整个工程很可能跑不起来,所以用 only-allow
库去限制包管理器,当用了其他包管理器,会直接抛异常退出进程。
Turborepo
在项目开发和打包发布,必须先启动 icons
和 components
项目编译构建,才能运行 docs
文档,如果使用 pnpm
构建,可能需要使用 -r
或者 &&
并行执行,如
{
"scripts": {
"build": "pnpm -r --parallel --filter=./packages/* run build",
"test": "pnpm -r --parallel --filter=./packages/* run test"
}
}
Pnpm 给我们提供的 -r 参数递归执行 NPM scripts,但是它不能按照先后顺序执行串行的任务,并且
-r
过于简单粗暴,有些模块明明没有修改代码,任务还是全量执行,影响 CI 构建效率。
专业的事交给专业的工具去解决,而 Turborepo 就非常擅长实现任务编排
什么是Turborepo?
Turborepo 是一个高性能的 JavaScript 和 TypeScript 项目构建系统,采用Go语言实现,所以在语言层面上就具有一定的性能优势,可以大大提高monorepo项目的构建速度。
在开发层面, Turborepo抽象出所有繁琐的配置、脚本和工具,减少项目配置的复杂性,可以让我们专注于业务的开发,并且支持使用 Yarn、Npm、Pnpm
TurboRepo的优势
1、多任务并行处理
Turbo支持多个任务的并行运行,我们在对多个子包,编译打包的过程中,turbo会同时进行多个任务的处理
对于项目中 A 依赖于 B,B 依赖于 C,构建串行顺序为 C、B、A。Turbo它能够有效地安排任务类似于瀑布可以同时异步执行多个任务,而 lerna
一次只能执行一项任务 所以Turbo的性能不言而喻。
2、更快的增量构建
如果我们的项目过大,构建多个子包会造成时间和性能的浪费,turborepo中的缓存机制 可以帮助我们记住构建内容 并且跳过已经计算过的内容,优化打包效率。
3、任务管道
用配置文件定义任务之间的关系,然后让Turborepo优化构建内容和时间。
4、远程云缓存
Turbo通过其远程缓存功能,团队成员、CI/CD 共享远程构建缓存,以实现更快的构建。
安装到项目
1、在项目根目录下,安装turbo依赖
pnpm i turbo --save-dev -w
2、在根目录下添加 turbo.json
配置文件,向 pipeline
字段中配置 npm scripts
中的命令,比如 dev
, build
命令
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
3、在根目录 package
配置 scripts
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build"
}
以上 Turborepo 项目就简单配置完成了,Turbo 和 Pnpm Workspace 很好的结合起来管理 monorepo 项目
Turbo 开发环境
当执行 npm run dev
命令,Turbo 会分析 Package 包的依赖关系,运行 @swc-ui/icons
、@swc-ui/components
docs
开发环境,通过 turbo.json
配置一行命令就启动了开发环境,不需要手动去执行 icon、components、docs的命令
Turbo 构建打包
Turbo 构建提供了缓存,当执行 npm run build
全部构建需要花费1分钟06秒 ,第二次构建修改了一个包,花费了 30 秒,第三次没有修改源码重新构建,1秒内构建完,明显可以感受到 Turbo 缓存构建的优势。
如对新技术开发组件库感兴趣,也欢迎加入stencil-component-ui,给个 star 鼓励一下 👏👏