背景
随着前端工程越来越大,模块越来越多,代码复用性越来越强,模块之间的依赖关系也越来越复杂,导致开发效率低下,维护成本高,代码质量难以保证。因此,我们需要一种能够有效管理这些模块的工具,以便更好地组织和管理我们的代码。
monorepo
是一种将多个模块放在同一个代码仓库中的管理方式。它可以将多个模块之间的依赖关系清晰地展示出来,使得模块之间的协作更加高效。同时,monorepo
还可以方便地进行模块的发布和更新,使得整个项目的管理更加简单。
multirepo vs monorepo
multirepo
优点:
- 每个模块独立,互不影响
- 模块可以独立发布和更新
- 模块可以独立部署
缺点:
- 模块之间的依赖关系复杂,难以管理
- 模块之间的协作效率低
- 模块之间的发布和更新需要手动处理

monorepo
优点:
- 模块之间的依赖关系清晰,易于管理
- 模块之间的协作效率高
- 模块之间的发布和更新可以自动化处理
缺点:
- 模块之间的耦合度高,难以独立发布和更新
- 模块之间的部署需要统一处理

方案选型
multirepo
和 monorepo
各有优缺点,选择哪种方式取决于项目的具体需求和团队的技术栈。如果项目规模较小,模块之间的依赖关系简单 ,可以选择 multirepo
;如果项目规模较大,模块之间的依赖关系复杂 ,可以选择 monorepo
。
常见 monorepo 管理工具
- pnpm
- npm
- yarn
- lerna
- nx
- rush
- ...
目录结构
shell
├── packages # 模块目录
│ ├── package-a
│ │ ├── src
│ │ ├── package.json
│ ├── package-b
│ │ ├── src
│ │ ├── package.json
|------ apps # 应用目录
│ ├── app-a
│ │ ├── src
│ │ ├── package.json
│ ├── app-b
│ │ ├── src
│ │ ├── package.json
├── packages.json # 项目根目录的 package.json
├── pnpm-workspace.yaml # pnpm 工作区配置文件
├── pnpm-lock.yaml
├── package.json
├── README.md
pnpm-workspace.yaml
pnpm-workspace.yaml
是 pnpm
的工作区配置文件,用于指定工作区中的模块和应用的目录。
yaml
packages:
- "packages/*"
- "apps/*"
环境版本锁定
在 monorepo
项目中,我们需要确保所有模块使用相同的环境版本,以避免出现环境不一致的问题。我们可以通过在 package.json
中指定 engines
字段来实现环境版本锁定。
json
// packages.json
"engines": {
"node": ">=14.0.0",
"pnpm": ">=7.0.0"
}
严格模式,确保安装不符合要求的版本报错显示。
yaml
#.npmrc
engine-strict=true
规范的统一化管理
TypeScript
在 monorepo
项目中,我们可以使用 TypeScript
来提高代码的可维护性和可读性。我们可以通过在 tsconfig.json
中指定 compilerOptions
字段来实现 TypeScript
的配置。
shell
pnpm -Dw add typescript @types/node
根目录下创建 tsconfig.json
文件,子包统一配置 TypeScript
编译选项。
json
// tsconfig.json
"compilerOptions": {
"module": "esnext",
"target": "esnext",
"strict": true,
"skipLibCheck": true,
"baseUrl": "",
}
每个子包下创建 tsconfig.json
文件,子包独立配置 TypeScript
编译选项。
json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationDir": "dist/types"
},
"include": ["src"]
}
prettier
prettier
是一个代码格式化工具,可以自动格式化代码,使其符合指定的代码风格。
shell
pnpm -Dw add prettier
根目录下创建 prettier.confiig.js
文件,子包统一配置 prettier
格式化选项。
js
module.exports = {
semi: true,
trailingComma: "es5",
singleQuote: true,
printWidth: 80,
tabWidth: 2,
};
排除文件,避免格式化不必要的文件。
yaml
#.prettierignore
node_modules
dist
git 提交规范
- 排除文件
yaml
#.gitignore
node_modules
dist
- commitizen
commitizen
是一个用于规范 git commit 提交的工具,可以确保提交信息符合一定的规范,从而提高代码的可维护性和可读性。
shell
pnpm -Dw add @commitlint/cli @commitlint/config-conventional commitizen cz-git
@commitlint/cli
是commitlint
的命令行工具,用于检查提交信息是否符合规范。@commitlint/config-conventional
是commitlint
的规范配置,用于指定提交信息的格式。commitizen
是一个用于规范git commit
提交的工具,可以确保提交信息符合一定的规范。cz-git
是一个用于规范git commit
提交的工具,可以确保提交信息符合一定的规范。
json
// package.json
"scripts": {
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-git"
}
}
创建 commitlint.config.js
文件,去定义 i 一些 commitlint
提交规范。
js
module.exports = {
extends: ["@commitlint/config-conventional"],
rules:{
"type-enum":[2, "always",]
}
};
- husky
husky
是一个用于规范 git commit
提交的工具,可以确保提交信息符合一定的规范,从而提高代码的可维护性和可读性。
shell
# 安装 husky
pnpm -Dw add husky
# 初始化 husky
pnpx husky init
配置
yaml
#!/usr/bin/env sh
pnpm run lint
以上是对整个工程做的一些统一的一些规范化管理,可以根据自己的项目需求,做更多的规范管理。
代码的统一化管理
统一打包
这个统一打包主要针对公共的库,比如 utils
、hooks
、components
等等,这些库是各个应用都会用到的,所以我们可以统一打包,然后发布到 npm
上,这样各个应用就可以直接安装使用了。
- 创建打包文件
可以再根目录创建 scripts
文件夹,然后创建 build.js
文件,用于打包公共库。
具体参考vue3的打包方式,使用rollup进行打包。build.js
- 添加脚本
在 package.json
中添加打包脚本。
json
"scripts": {
"build": "node scripts/build.js"
}
- 执行打包
shell
pnpm run build
建立包依赖
通过软链接的方式,将公共库链接到各个应用中,这样各个应用就可以直接使用公共库了。
json
// package.json
"dependencies": {
"package-a": "workspace:*"
}
发布
- 登录
shell
pnpm login
- 发布
shell
pnpm publish
总结
monorepo
是一种代码管理方案,是一个策略、思想,而不是一个工具。主要就是通过规范化、统一化的方式,来提高代码的可维护性和可读性,从而提高开发效率。以上只是简单的介绍了 monorepo
方案的实施,具体的实现还需要根据项目的需求和团队的技术栈来决定。Vue3
采用的就是 monorepo
方式进行项目代码管理。 有兴趣的可以对照 vue3源码 去了解具体的实施细节。