加载中...

使用 pnpm workspace 实现 monorepo 策略

什么是Monorepo?

Monorepo 将许多不同项目集中在一个代码仓库中进行管理,这些项目在某种程度上相互依赖,但在逻辑上是独立的,这意味着不同的团队可以独立地进行工作。 与之相对的是 Multirepo,每个项目都拥有单独的代码仓库。

Monorepo 的优点:

  1. 代码共享和复用:在单一代码库中,不同项目和模块可以轻松地共享和复用代码,降低了重复开发的成本。
  2. 依赖管理:Monorepo 可以使开发人员更容易地管理项目间的依赖关系,减少了版本冲突和升级问题。
  3. 原子提交:Monorepo 允许开发人员在一个提交中更新多个项目或模块,这有助于保持代码的一致性。
  4. 更简洁的工作流程:使用 Monorepo 可以简化构建、测试和部署等工作流程,提高开发效率。

Monorepo 的缺点:

  1. 代码库规模:随着项目和代码的增长,Monorepo 的规模可能变得庞大,从而影响性能和存储需求。
  2. 权限管理:在一个大型代码库中管理访问权限可能变得复杂,特别是在多团队协作的情况下。
  3. 潜在的耦合:由于代码位于同一仓库中,可能导致不同项目之间的耦合过于紧密,影响项目的独立性和灵活性。

一些知名的开源项目,如 Next.jsViteElement Plus,都采用了 Monorepo 策略。在选择 Monorepo 之前,需要权衡其优缺点,并考虑它是否适用于特定的开发环境和需求。

monorepo 方案实践

monorepo可以使用 lerna,也可以使用更简洁的 yarn、pnpm,但是 pnpm 相对于 yarn 包管理机制更加强大完善,所以我们采用 pnpm 实现 monorepo。

创建项目

首先,全局安装pnpm,创建项目目录,然后使用 pnpm init命令创建一个新的空项目。

sh
代码解读
复制代码
npm i -g pnpm mkdir xxxx-monorepo cd ./xxxx-monorepo pnpm init

创建 pnpm Workspaces

根目录下必须有 pnpm-workspace.yaml 文件,它定义了工作空间的根目录

yaml
代码解读
复制代码
packages: - packages/*

创建业务项目

使用 pnpm create vite命令在 packages 目录下创建project1项目

sh
代码解读
复制代码
mkdir packages cd packages pnpm create vite project1 --template react cd project1 pnpm install pnpm dev

创建使用公共 utils

在 packages 目录下创建公共 utils 目录

sh
代码解读
复制代码
mkdir utils

添加 package.json 文件

json
代码解读
复制代码
{ "name": "@xxxx/utils", "main": "index.js", "version": "0.0.1" }

创建 log.js 文件

js
代码解读
复制代码
const log = (text) => { console.log(text); }; export default log;

创建 index.js 入口文件,并导出 log 方法

js
代码解读
复制代码
import log from ''./log.js''; export { log };

进入 packages/project1,添加 @xxxx/utils 依赖

sh
代码解读
复制代码
cd ../packages/project1 pnpm add @xxxx/utils

在 App.jsx 中引用 log 方法

js
代码解读
复制代码
import { log } from ''@xxxx/utils''; function App() { return ( <div className="App"> <button onClick={() => { log(''onClick''); }} ></button> </div> ); } export default App;

创建使用公共组件

在 packages 目录下创建 components 目录

sh
代码解读
复制代码
mkdir packages/components cd packages/components

添加 package.json 文件

json
代码解读
复制代码
{ "name": "@xxxx/components", "main": "index.js", "version": "0.0.1" }

创建 Button 组件

css
代码解读
复制代码
packages/ ├── components/ │ └── Button/ │ └───index.tsx ├── utils/ └── project1/

在 index.tsx 文件中编写 Button 组件的代码。

jsx
代码解读
复制代码
export default function Button(props) { return <button style={{ background: ''red'' }} {...props} />; }

创建 index.js 入口文件,并导出 Button 组件

js
代码解读
复制代码
import Button from ''./Button''; export { Button };

进入 packages/project1,添加 @xxxx/components 依赖

sh
代码解读
复制代码
cd ../packages/project1 pnpm add @xxxx/components

在 App.jsx 中引用 Button 组件

js
代码解读
复制代码
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;

项目地址

pnpm-monorepo-demo - StackBlitz