各位好,今天咱们来聊聊大型 Vue Monorepo 项目里的那些事儿,特别是怎么用 pnpm
和 Turborepo
这两把刷子来管理依赖、构建和部署流程。 保证听完之后,你的项目也能像整理过的房间一样,井井有条!
Monorepo 是个啥?为啥要用它?
首先,咱们得先搞清楚 Monorepo 是个什么玩意儿。简单来说,就是把多个项目(或者说 Package)的代码都放在同一个代码仓库里。
那为啥要用 Monorepo 呢?好处多多:
- 代码复用更容易: 多个项目之间共享代码,不用到处复制粘贴,减少重复代码。
- 依赖管理更简单: 统一管理依赖,避免版本冲突,升级依赖也更方便。
- 原子性变更: 修改一个公共库,所有依赖它的项目都可以同步更新,保证一致性。
- 协作更高效: 团队成员更容易了解整个项目,促进协作。
当然,Monorepo 也有缺点:
- 仓库体积大: 所有代码都在一起,仓库体积可能会比较大。
- 构建时间长: 构建整个仓库可能需要比较长的时间。
- 权限管理复杂: 需要更精细的权限管理,防止误操作。
不过,有了 pnpm
和 Turborepo
,这些缺点都可以得到很好的解决。
pnpm
:高效的依赖管理器
pnpm
是一个快速、节省磁盘空间的 Node.js 包管理器。它最大的特点就是使用硬链接和符号链接来共享依赖,避免重复安装,大大节省磁盘空间。
pnpm
的优势:
- 节省磁盘空间: 避免重复安装依赖,节省磁盘空间。
- 安装速度快: 利用缓存和硬链接,安装速度更快。
- 更强的安全性: 默认开启严格模式,避免幽灵依赖。
- 支持 Monorepo: 内置支持 Monorepo,方便管理多个项目。
如何在 Monorepo 中使用 pnpm
?
-
安装
pnpm
:npm install -g pnpm
-
初始化 Monorepo:
在项目根目录下创建一个
pnpm-workspace.yaml
文件,声明 Monorepo 的结构。packages: - 'packages/*' # 所有在 packages 目录下的项目 - 'apps/*' # 所有在 apps 目录下的项目
这个文件告诉
pnpm
,哪些目录是独立的 Package。 -
安装依赖:
在项目根目录下运行
pnpm install
,pnpm
会根据pnpm-workspace.yaml
文件,为每个 Package 安装依赖。 -
添加/删除依赖:
-
为单个 Package 添加依赖:
pnpm add <package-name> -w # -w 代表在根目录下添加 pnpm add <package-name> -P # -P 添加到生产依赖中 pnpm add <package-name> -D # -D 添加到开发依赖中 pnpm add <package-name> --filter <package-name> # 只给指定package安装依赖
-
为所有 Package 添加依赖:
pnpm add <package-name> -r # -r 代表递归地添加到所有 Package 中
-
-
运行脚本:
-
运行单个 Package 的脚本:
pnpm --filter <package-name> <script-name>
-
运行所有 Package 的脚本:
pnpm run <script-name> -r
-
Turborepo
:智能的构建工具
Turborepo
是一个高性能的构建工具,专门为 Monorepo 设计。它利用缓存和并行构建,大大缩短构建时间。
Turborepo
的优势:
- 增量构建: 只构建发生变化的项目,避免重复构建。
- 并行构建: 同时构建多个项目,提高构建效率。
- 缓存: 缓存构建结果,下次构建时直接使用缓存,避免重复构建。
- 依赖分析: 自动分析项目之间的依赖关系,确定构建顺序。
如何在 Monorepo 中使用 Turborepo
?
-
安装
Turborepo
:npm install -g turbo
-
初始化
Turborepo
:在项目根目录下运行
turbo init
,Turborepo
会自动创建turbo.json
文件。 -
配置
turbo.json
:turbo.json
文件是Turborepo
的配置文件,用于定义构建任务、依赖关系和缓存策略。{ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], // 依赖其他 package 的 build 任务 "outputs": ["dist/**", "build/**", ".next/**"], // 构建输出的文件 "cache": { "glob": ["**/*"], "hash": ["package.json", "tsconfig.json", "src/**/*"] } }, "test": { "dependsOn": ["build"], // 依赖 build 任务 "inputs": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts", "test/**/*.tsx"], "cache": { "glob": ["coverage/**"] } }, "lint": {}, "dev": { "cache": false, // 开发环境不缓存 "persistent": true // 在后台运行 } } }
pipeline
:定义构建任务的流程。dependsOn
:定义任务之间的依赖关系。^
表示依赖所有依赖的 Package 的任务。outputs
:定义任务的输出文件,用于缓存。cache
:配置缓存策略。inputs
: 定义任务的输入文件,用于检测文件是否变化。persistent
: 定义任务是否在后台运行。
-
运行构建任务:
turbo run build # 构建所有项目 turbo run test # 测试所有项目 turbo run lint # lint所有项目 turbo run dev # 运行所有项目的dev任务
Turborepo
会根据turbo.json
文件的配置,自动分析项目之间的依赖关系,并并行构建。
pnpm
+ Turborepo
:最佳拍档
pnpm
和 Turborepo
搭配使用,可以发挥更大的威力。pnpm
负责依赖管理,Turborepo
负责构建。两者配合,可以大大提高 Monorepo 项目的开发效率。
一个完整的示例
假设我们有一个 Monorepo 项目,包含以下几个 Package:
packages/ui
:UI 组件库packages/utils
:工具函数库apps/web
:Web 应用apps/mobile
:移动应用
-
创建项目结构:
monorepo-example/ ├── pnpm-workspace.yaml ├── turbo.json ├── packages/ │ ├── ui/ │ │ ├── package.json │ │ └── src/ │ ├── utils/ │ │ ├── package.json │ │ └── src/ ├── apps/ │ ├── web/ │ │ ├── package.json │ │ └── src/ │ ├── mobile/ │ │ ├── package.json │ │ └── src/
-
pnpm-workspace.yaml
:packages: - 'packages/*' - 'apps/*'
-
turbo.json
:{ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", "build/**", ".next/**"], "cache": { "glob": ["**/*"], "hash": ["package.json", "tsconfig.json", "src/**/*"] } }, "test": { "dependsOn": ["build"], "inputs": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts", "test/**/*.tsx"], "cache": { "glob": ["coverage/**"] } }, "lint": {}, "dev": { "cache": false, "persistent": true } } }
-
package.json
(根目录):{ "name": "monorepo-example", "private": true, "scripts": { "build": "turbo run build", "test": "turbo run test", "lint": "turbo run lint", "dev": "turbo run dev" }, "devDependencies": { "turbo": "^1.10.16" } }
-
package.json
(packages/ui):{ "name": "@monorepo-example/ui", "version": "0.0.1", "scripts": { "build": "vue-cli-service build", "test": "jest" }, "dependencies": { "@monorepo-example/utils": "workspace:*" }, "devDependencies": { "@vue/cli-service": "^5.0.0", "jest": "^29.0.0" } }
-
package.json
(packages/utils):{ "name": "@monorepo-example/utils", "version": "0.0.1", "scripts": { "build": "tsc", "test": "jest" }, "devDependencies": { "typescript": "^5.0.0", "jest": "^29.0.0" } }
-
package.json
(apps/web):{ "name": "@monorepo-example/web", "version": "0.0.1", "scripts": { "dev": "vue-cli-service serve", "build": "vue-cli-service build", "test": "jest" }, "dependencies": { "@monorepo-example/ui": "workspace:*" }, "devDependencies": { "@vue/cli-service": "^5.0.0", "jest": "^29.0.0" } }
-
package.json
(apps/mobile):{ "name": "@monorepo-example/mobile", "version": "0.0.1", "scripts": { "dev": "react-native start", "build": "react-native build", "test": "jest" }, "dependencies": { "@monorepo-example/ui": "workspace:*" }, "devDependencies": { "react-native": "^0.72.0", "jest": "^29.0.0" } }
注意:
workspace:*
表示依赖本地的 Package。 -
运行构建:
pnpm install # 安装依赖 pnpm run build # 构建所有项目
Turborepo
会自动分析项目之间的依赖关系,先构建packages/utils
,然后构建packages/ui
,最后构建apps/web
和apps/mobile
。
部署流程
部署流程和普通的项目没有太大的区别。你可以使用 Docker、Kubernetes 等工具来部署 Monorepo 项目。
- 构建镜像: 为每个应用构建 Docker 镜像。
- 部署应用: 将 Docker 镜像部署到 Kubernetes 集群或其他云平台上。
总结
pnpm
和 Turborepo
是管理大型 Vue Monorepo 项目的利器。pnpm
负责依赖管理,Turborepo
负责构建。两者配合,可以大大提高开发效率,让你的项目更加井井有条。
希望今天的讲座对你有所帮助! 祝大家的项目都能告别“脏乱差”,走向“高大上”!