JavaScript内核与高级编程之:`JavaScript`的`monorepo`架构:`pnpm`、`yarn` 和 `lerna` 在多包管理中的实践。

各位靓仔靓女们,欢迎来到今天的“前端武林大会”,我是今天的说书人(兼码农)老王。今天咱们不聊刀光剑影,聊聊代码江湖里的“一统天下”——Monorepo。

话说天下大势,分久必合,合久必分。前端项目也一样,一开始小打小闹,一个项目一个小仓库,日子过得挺滋润。但随着业务规模的扩大,项目越来越多,代码复用成了难题,版本管理也乱成一锅粥。这个时候,Monorepo 就闪亮登场了,它就像一个武林盟主,把所有相关的项目都纳入麾下,统一管理。

今天,我们就来扒一扒 Monorepo 的那些事儿,重点说说三大门派:pnpmyarnlerna,看看它们是如何在多包管理中各显神通的。

一、啥是 Monorepo?

简单来说,Monorepo 就是在一个代码仓库里管理多个项目。这些项目可以是库、框架、工具、应用程序,甚至是整个组织的代码。

Monorepo 的好处:

  • 代码复用: 不同的项目可以轻松地共享代码,避免重复造轮子。
  • 依赖管理: 统一管理所有项目的依赖,避免版本冲突和依赖地狱。
  • 原子提交: 可以一次性提交多个项目的修改,保证代码的一致性。
  • 协同开发: 团队成员可以更容易地理解整个项目的结构和依赖关系。
  • 构建和测试: 可以一次性构建和测试所有项目,提高效率。

Monorepo 的坏处:

  • 仓库体积: 整个仓库的体积可能会很大,clone和checkout可能比较慢。
  • 权限管理: 需要更精细的权限管理,避免不必要的访问。
  • 构建复杂度: 构建过程可能比较复杂,需要更高效的构建工具。
  • 学习成本: 团队成员需要学习新的工具和流程。

二、三大门派:pnpmyarnlerna

这三大门派在 Monorepo 的江湖里,各有千秋,各有所长。

  • pnpm 后起之秀,以其高效的磁盘空间利用率和快速的安装速度著称。
  • yarn 曾经的霸主,现在也在不断进化,拥有强大的插件生态系统。
  • lerna 专门为 Monorepo 设计的工具,可以帮助你管理和发布多个包。

三、pnpm:磁盘空间的救星

pnpm 的最大特点就是它的存储方式。它使用了一种叫做 "content-addressable storage" 的技术,简单来说,就是把每个包都存储在一个全局的 "store" 里,然后在每个项目的 node_modules 目录里创建指向这些包的硬链接或符号链接。

这样做的优点是:

  • 节省磁盘空间: 相同的包只需要存储一份,不同的项目可以共享使用。
  • 安装速度快: 如果包已经存在于 "store" 里,直接创建链接即可,不需要重新下载。
  • 避免依赖地狱: 由于依赖包是共享的,可以避免不同项目之间的依赖冲突。

pnpm 在 Monorepo 中的实践:

  1. 初始化 Monorepo:

    mkdir my-monorepo
    cd my-monorepo
    pnpm init
  2. 创建 pnpm-workspace.yaml 文件:

    这个文件用来告诉 pnpm 哪些目录是你的项目。

    packages:
      - 'packages/*'

    这个配置表示 packages 目录下的所有子目录都是项目。

  3. 创建项目目录:

    mkdir packages
    mkdir packages/package-a
    mkdir packages/package-b
    cd packages/package-a
    pnpm init -y
    cd ../package-b
    pnpm init -y
  4. 安装依赖:

    cd my-monorepo
    pnpm add lodash -w # -w 表示在 workspace 根目录下安装,相当于全局安装
    cd packages/package-a
    pnpm add react react-dom
    cd ../package-b
    pnpm add axios
  5. 在项目中使用依赖:

    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 中的实践:

  1. 初始化 Monorepo:

    mkdir my-monorepo
    cd my-monorepo
    yarn init -y
  2. 启用 workspace 功能:

    package.json 文件中添加 workspaces 字段:

    {
      "private": true,
      "workspaces": [
        "packages/*"
      ]
    }

    这个配置表示 packages 目录下的所有子目录都是 workspace。

  3. 创建项目目录:

    mkdir packages
    mkdir packages/package-a
    mkdir packages/package-b
    cd packages/package-a
    yarn init -y
    cd ../package-b
    yarn init -y
  4. 安装依赖:

    cd my-monorepo
    yarn add lodash -W # -W 表示在 workspace 根目录下安装,相当于全局安装
    cd packages/package-a
    yarn add react react-dom
    cd ../package-b
    yarn add axios
  5. 在项目中使用依赖:

    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 中的实践:

  1. 初始化 Monorepo:

    mkdir my-monorepo
    cd my-monorepo
    yarn init -y
    yarn add lerna --dev
  2. 初始化 lerna

    npx lerna init

    这会创建一个 lerna.json 文件,用于配置 lerna

  3. 配置 lerna.json

    {
      "packages": [
        "packages/*"
      ],
      "version": "independent",
      "npmClient": "yarn",
      "useWorkspaces": true
    }
    • packages: 指定哪些目录是包。
    • version: 版本管理模式,independent 表示每个包独立管理版本,fixed 表示所有包使用相同的版本。
    • npmClient: 使用哪个包管理器,这里使用 yarn
    • useWorkspaces: 是否使用 yarn 的 workspace 功能。
  4. 创建项目目录:

    mkdir packages
    mkdir packages/package-a
    mkdir packages/package-b
    cd packages/package-a
    yarn init -y
    cd ../package-b
    yarn init -y
  5. 安装依赖:

    yarn 一样,直接在项目中使用依赖即可。

  6. 发布包:

    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 设计
擅长领域 依赖管理和安装 依赖管理和插件扩展 包的版本管理和发布
学习成本 较低 较低 较高
是否需要其他工具配合 通常不需要 通常不需要 通常需要 pnpmyarn 配合
  • 如果你追求极致的磁盘空间利用率和安装速度,pnpm 是你的不二之选。
  • 如果你需要丰富的插件生态系统,yarn 仍然是一个不错的选择。
  • 如果你需要管理和发布多个包,lerna 可以帮助你简化流程。

当然,你也可以根据自己的实际情况,选择将它们组合使用。例如,使用 pnpmyarn 管理依赖,然后使用 lerna 发布包。

七、Monorepo 的一些最佳实践

  • 明确项目结构: 在 Monorepo 中,清晰的项目结构至关重要。建议将相关的项目放在同一个目录下,并使用有意义的命名。
  • 统一依赖管理: 尽量统一所有项目的依赖版本,避免版本冲突。
  • 自动化构建和测试: 使用 CI/CD 工具,自动化构建和测试所有项目,确保代码质量。
  • 代码审查: 严格的代码审查可以帮助你发现潜在的问题,并保证代码的一致性。
  • 文档: 编写清晰的文档,帮助团队成员理解项目的结构和依赖关系。

好了,今天的“前端武林大会”就到这里了。希望通过今天的讲解,大家对 Monorepo 和三大门派有了更深入的了解。记住,选择适合自己的工具,才能在代码江湖里笑傲群雄! 各位,下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注