各位靓仔靓女们,欢迎来到今天的“前端武林大会”,我是今天的说书人(兼码农)老王。今天咱们不聊刀光剑影,聊聊代码江湖里的“一统天下”——Monorepo。
话说天下大势,分久必合,合久必分。前端项目也一样,一开始小打小闹,一个项目一个小仓库,日子过得挺滋润。但随着业务规模的扩大,项目越来越多,代码复用成了难题,版本管理也乱成一锅粥。这个时候,Monorepo 就闪亮登场了,它就像一个武林盟主,把所有相关的项目都纳入麾下,统一管理。
今天,我们就来扒一扒 Monorepo 的那些事儿,重点说说三大门派:pnpm
、yarn
和 lerna
,看看它们是如何在多包管理中各显神通的。
一、啥是 Monorepo?
简单来说,Monorepo 就是在一个代码仓库里管理多个项目。这些项目可以是库、框架、工具、应用程序,甚至是整个组织的代码。
Monorepo 的好处:
- 代码复用: 不同的项目可以轻松地共享代码,避免重复造轮子。
- 依赖管理: 统一管理所有项目的依赖,避免版本冲突和依赖地狱。
- 原子提交: 可以一次性提交多个项目的修改,保证代码的一致性。
- 协同开发: 团队成员可以更容易地理解整个项目的结构和依赖关系。
- 构建和测试: 可以一次性构建和测试所有项目,提高效率。
Monorepo 的坏处:
- 仓库体积: 整个仓库的体积可能会很大,clone和checkout可能比较慢。
- 权限管理: 需要更精细的权限管理,避免不必要的访问。
- 构建复杂度: 构建过程可能比较复杂,需要更高效的构建工具。
- 学习成本: 团队成员需要学习新的工具和流程。
二、三大门派:pnpm
、yarn
和 lerna
这三大门派在 Monorepo 的江湖里,各有千秋,各有所长。
pnpm
: 后起之秀,以其高效的磁盘空间利用率和快速的安装速度著称。yarn
: 曾经的霸主,现在也在不断进化,拥有强大的插件生态系统。lerna
: 专门为 Monorepo 设计的工具,可以帮助你管理和发布多个包。
三、pnpm
:磁盘空间的救星
pnpm
的最大特点就是它的存储方式。它使用了一种叫做 "content-addressable storage" 的技术,简单来说,就是把每个包都存储在一个全局的 "store" 里,然后在每个项目的 node_modules
目录里创建指向这些包的硬链接或符号链接。
这样做的优点是:
- 节省磁盘空间: 相同的包只需要存储一份,不同的项目可以共享使用。
- 安装速度快: 如果包已经存在于 "store" 里,直接创建链接即可,不需要重新下载。
- 避免依赖地狱: 由于依赖包是共享的,可以避免不同项目之间的依赖冲突。
pnpm
在 Monorepo 中的实践:
-
初始化 Monorepo:
mkdir my-monorepo cd my-monorepo pnpm init
-
创建
pnpm-workspace.yaml
文件:这个文件用来告诉
pnpm
哪些目录是你的项目。packages: - 'packages/*'
这个配置表示
packages
目录下的所有子目录都是项目。 -
创建项目目录:
mkdir packages mkdir packages/package-a mkdir packages/package-b cd packages/package-a pnpm init -y cd ../package-b pnpm init -y
-
安装依赖:
cd my-monorepo pnpm add lodash -w # -w 表示在 workspace 根目录下安装,相当于全局安装 cd packages/package-a pnpm add react react-dom cd ../package-b pnpm add axios
-
在项目中使用依赖:
packages/package-a/index.js
import React from 'react'; import ReactDOM from 'react-dom'; import _ from 'lodash'; function App() { return <h1>Hello, {_.upperCase('World')}!</h1>; } ReactDOM.render(<App />, document.getElementById('root'));
packages/package-b/index.js
import axios from 'axios'; import _ from 'lodash'; async function fetchData() { const response = await axios.get('https://api.example.com/data'); console.log(_.get(response, 'data')); } fetchData();
pnpm
常用命令:
命令 | 描述 |
---|---|
pnpm install |
安装所有依赖 |
pnpm add <pkg> |
在当前项目安装依赖 <pkg> |
pnpm add <pkg> -w |
在 workspace 根目录安装依赖 <pkg> ,相当于全局安装 |
pnpm run <script> |
运行当前项目定义的脚本 |
pnpm --filter <pkg> run <script> |
运行指定项目 <pkg> 定义的脚本,<pkg> 可以是包名,也可以是目录名。 |
pnpm -r run <script> |
运行所有项目定义的脚本。 |
四、yarn
:插件生态的王者
yarn
曾经是前端包管理工具的霸主,现在虽然地位有所动摇,但仍然拥有强大的用户基础和丰富的插件生态系统。yarn
在 Monorepo 中的实践也很成熟。
yarn
在 Monorepo 中的实践:
-
初始化 Monorepo:
mkdir my-monorepo cd my-monorepo yarn init -y
-
启用 workspace 功能:
在
package.json
文件中添加workspaces
字段:{ "private": true, "workspaces": [ "packages/*" ] }
这个配置表示
packages
目录下的所有子目录都是 workspace。 -
创建项目目录:
mkdir packages mkdir packages/package-a mkdir packages/package-b cd packages/package-a yarn init -y cd ../package-b yarn init -y
-
安装依赖:
cd my-monorepo yarn add lodash -W # -W 表示在 workspace 根目录下安装,相当于全局安装 cd packages/package-a yarn add react react-dom cd ../package-b yarn add axios
-
在项目中使用依赖:
和
pnpm
一样,直接在项目中使用依赖即可。
yarn
常用命令:
命令 | 描述 |
---|---|
yarn install |
安装所有依赖 |
yarn add <pkg> |
在当前项目安装依赖 <pkg> |
yarn add <pkg> -W |
在 workspace 根目录安装依赖 <pkg> ,相当于全局安装 |
yarn run <script> |
运行当前项目定义的脚本 |
yarn workspace <pkg> run <script> |
运行指定项目 <pkg> 定义的脚本,<pkg> 是包名 |
yarn workspaces run <script> |
运行所有项目定义的脚本。 |
五、lerna
:Monorepo 的专属管家
lerna
是一个专门为 Monorepo 设计的工具,它可以帮助你管理和发布多个包。lerna
主要关注的是包的版本管理和发布流程。
lerna
在 Monorepo 中的实践:
-
初始化 Monorepo:
mkdir my-monorepo cd my-monorepo yarn init -y yarn add lerna --dev
-
初始化
lerna
:npx lerna init
这会创建一个
lerna.json
文件,用于配置lerna
。 -
配置
lerna.json
:{ "packages": [ "packages/*" ], "version": "independent", "npmClient": "yarn", "useWorkspaces": true }
packages
: 指定哪些目录是包。version
: 版本管理模式,independent
表示每个包独立管理版本,fixed
表示所有包使用相同的版本。npmClient
: 使用哪个包管理器,这里使用yarn
。useWorkspaces
: 是否使用yarn
的 workspace 功能。
-
创建项目目录:
mkdir packages mkdir packages/package-a mkdir packages/package-b cd packages/package-a yarn init -y cd ../package-b yarn init -y
-
安装依赖:
和
yarn
一样,直接在项目中使用依赖即可。 -
发布包:
lerna publish
这个命令会自动检测哪些包有修改,然后提示你选择新的版本号,最后发布到 npm。
lerna
常用命令:
命令 | 描述 |
---|---|
lerna init |
初始化 lerna |
lerna bootstrap |
安装所有依赖并链接本地包,相当于 yarn install + lerna link |
lerna changed |
列出自上次发布以来发生更改的包 |
lerna publish |
发布包 |
lerna run <script> |
在所有包含该脚本的包中运行 npm 脚本。例如:lerna run test |
lerna exec -- <command> |
在一个或多个包中执行任意 shell 命令。例如: lerna exec -- rm -rf ./node_modules 。注意 -- 之后的内容会被当做命令执行。 |
六、总结:选择哪个门派?
特性 | pnpm |
yarn |
lerna |
---|---|---|---|
磁盘空间利用率 | 极高 | 较高 | N/A |
安装速度 | 快 | 较快 | N/A |
插件生态 | 较弱 | 强大 | N/A |
Monorepo 支持 | 原生支持 | 通过 workspace 支持 | 专门为 Monorepo 设计 |
擅长领域 | 依赖管理和安装 | 依赖管理和插件扩展 | 包的版本管理和发布 |
学习成本 | 较低 | 较低 | 较高 |
是否需要其他工具配合 | 通常不需要 | 通常不需要 | 通常需要 pnpm 或 yarn 配合 |
- 如果你追求极致的磁盘空间利用率和安装速度,
pnpm
是你的不二之选。 - 如果你需要丰富的插件生态系统,
yarn
仍然是一个不错的选择。 - 如果你需要管理和发布多个包,
lerna
可以帮助你简化流程。
当然,你也可以根据自己的实际情况,选择将它们组合使用。例如,使用 pnpm
或 yarn
管理依赖,然后使用 lerna
发布包。
七、Monorepo 的一些最佳实践
- 明确项目结构: 在 Monorepo 中,清晰的项目结构至关重要。建议将相关的项目放在同一个目录下,并使用有意义的命名。
- 统一依赖管理: 尽量统一所有项目的依赖版本,避免版本冲突。
- 自动化构建和测试: 使用 CI/CD 工具,自动化构建和测试所有项目,确保代码质量。
- 代码审查: 严格的代码审查可以帮助你发现潜在的问题,并保证代码的一致性。
- 文档: 编写清晰的文档,帮助团队成员理解项目的结构和依赖关系。
好了,今天的“前端武林大会”就到这里了。希望通过今天的讲解,大家对 Monorepo 和三大门派有了更深入的了解。记住,选择适合自己的工具,才能在代码江湖里笑傲群雄! 各位,下次再见!