什么是Monorepo?
Monorepo 将许多不同项目集中在一个代码仓库中进行管理,这些项目在某种程度上相互依赖,但在逻辑上是独立的,这意味着不同的团队可以独立地进行工作。 与之相对的是 Multirepo,每个项目都拥有单独的代码仓库。
Monorepo 的优点:
- 代码共享和复用:在单一代码库中,不同项目和模块可以轻松地共享和复用代码,降低了重复开发的成本。
- 依赖管理:Monorepo 可以使开发人员更容易地管理项目间的依赖关系,减少了版本冲突和升级问题。
- 原子提交:Monorepo 允许开发人员在一个提交中更新多个项目或模块,这有助于保持代码的一致性。
- 更简洁的工作流程:使用 Monorepo 可以简化构建、测试和部署等工作流程,提高开发效率。
Monorepo 的缺点:
- 代码库规模:随着项目和代码的增长,Monorepo 的规模可能变得庞大,从而影响性能和存储需求。
- 权限管理:在一个大型代码库中管理访问权限可能变得复杂,特别是在多团队协作的情况下。
- 潜在的耦合:由于代码位于同一仓库中,可能导致不同项目之间的耦合过于紧密,影响项目的独立性和灵活性。
一些知名的开源项目,如 Next.js、Vite 和Element Plus,都采用了 Monorepo 策略。在选择 Monorepo 之前,需要权衡其优缺点,并考虑它是否适用于特定的开发环境和需求。
monorepo 方案实践
monorepo可以使用 lerna,也可以使用更简洁的 yarn、pnpm,但是 pnpm 相对于 yarn 包管理机制更加强大完善,所以我们采用 pnpm 实现 monorepo。
创建项目
首先,全局安装pnpm,创建项目目录,然后使用 pnpm init命令创建一个新的空项目。
npm i -g pnpm
mkdir xxxx-monorepo
cd ./xxxx-monorepo
pnpm init
创建 pnpm Workspaces
根目录下必须有 pnpm-workspace.yaml 文件,它定义了工作空间的根目录
packages:
- packages/*
创建业务项目
使用 pnpm create vite命令在 packages 目录下创建project1项目
mkdir packages
cd packages
pnpm create vite project1 --template react
cd project1
pnpm install
pnpm dev
创建使用公共 utils
在 packages 目录下创建公共 utils 目录
mkdir utils
添加 package.json 文件
{
"name": "@xxxx/utils",
"main": "index.js",
"version": "0.0.1"
}
创建 log.js 文件
const log = (text) => {
console.log(text);
};
export default log;
创建 index.js 入口文件,并导出 log 方法
import log from './log.js';
export { log };
进入 packages/project1,添加 @xxxx/utils 依赖
cd ../packages/project1
pnpm add @xxxx/utils
在 App.jsx 中引用 log 方法
import { log } from '@xxxx/utils';
function App() {
return (
<div className="App">
<button
onClick={() => {
log('onClick');
}}
></button>
</div>
);
}
export default App;
创建使用公共组件
在 packages 目录下创建 components 目录
mkdir packages/components
cd packages/components
添加 package.json 文件
{
"name": "@xxxx/components",
"main": "index.js",
"version": "0.0.1"
}
创建 Button 组件
packages/
├── components/
│ └── Button/
│ └───index.tsx
├── utils/
└── project1/
在 index.tsx 文件中编写 Button 组件的代码。
export default function Button(props) {
return <button style={{ background: 'red' }} {...props} />;
}
创建 index.js 入口文件,并导出 Button 组件
import Button from './Button';
export { Button };
进入 packages/project1,添加 @xxxx/components 依赖
cd ../packages/project1
pnpm add @xxxx/components
在 App.jsx 中引用 Button 组件
import { log } from '@xxxx/utils'
import { Button } from '@xxxx/components';
function App() {
return (
<div className="App">
<Button
onClick={() => {
log('onClick');
}}
></Button>
</div>
);
}
export default App;