自学内容网 自学内容网

年久失修!monorepo依赖管理失控补救方法

背景

开发monorepo项目时,因为文件结构较为复杂,时间久了往往会出现一些在所难免的问题,导致项目冗余度高,性能差,主要分为以下几类问题:

  1. 依赖管理不当,项目中存在大量重复依赖,无用依赖

  2. 存在多个不同版本的同一包

  3. 合码后pnpm-lock被忽略更新,导致团队开发时版本冲突

复现场景:

大量无用依赖和重复依赖怎么来的?

无用依赖(Unused Dependencies)

原因:

  • 依赖未清理:项目中可能有一些包已经不再使用某些依赖,但没有及时从 package.json 中删除这些不再使用的依赖,导致它们被保留在项目中。

  • 历史包依赖未清理:在开发过程中,如果有些包或者模块被拆分、重构或删除,之前的依赖可能仍然保留在 package.json 中,但这些依赖已经不再被任何代码实际使用。

场景示例:

假设原来项目中有包 C 和 D,其中包 C 使用了 axios,但后来包 C 被拆分成两个小模块,且这两个模块中都不再使用 axios 了。此时 axios 依赖依然留在包 C 的 package.json 中,但实际上不再被任何代码使用。

// package.json of C (已不再使用 axios)
{
  "dependencies": {
    "axios": "^0.21.1"
  }
}

依赖版本不一致(Version Inconsistency)

原因:

  • 不同子包依赖不同版本的同一个库:如果不同的子包依赖同一库的不同版本,pnpm 会倾向于安装多个版本,以避免版本冲突。这可能会导致在 monorepo 中出现多个版本的相同库,增加了冗余。

场景示例:

如果包 A 和包 B 都依赖 react,但版本不同,pnpm 可能会安装两个版本的 react,这会导致重复依赖。

// package.json of A
{
  "dependencies": {
    "react": "^18.0.0"
  }
}
​
// package.json of B
{
  "dependencies": {
    "react": "^17.0.0"
  }
}

在这种情况下,pnpm 会将 react 18 和 17 的版本都安装到 node_modules 中。

pnpm 的 Hoisting 设置问题

原因:

  • Hoisting 配置不当:pnpm 默认使用 hoisting(提升)来将共享的依赖提升到根目录的 node_modules 中。如果 hoisting 配置不当,某些依赖可能不会被提升到根目录,而是被分散在各个子包中,从而导致重复的依赖和包体积冗余。

场景示例:

如果 pnpm 的 workspace 配置中,某些依赖没有正确提升到顶层,可能会导致每个子包都各自安装一份相同的依赖,而不是通过提升机制共享依赖。

// pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'libs/*'
hoist:
  - 'react'
  - 'lodash'

如果 reactlodash 在所有子包中都使用相同版本,pnpm 应该将它们提升到顶层。如果没有正确配置或不一致的配置,会导致重复安装。

忽视 Workspace Dependency(未共享依赖)

原因:

  • 跨包依赖未共享:如果某些子包之间的依赖没有共享,而是每个包都独立安装,可能导致重复安装和依赖不一致。例如,如果包 A 和包 B 都使用了包 C,但各自独立地安装了包 C 而没有通过 workspace 管理共享这些依赖,就会导致冗余依赖。

场景示例:

假设包 A 和包 B 都依赖于包 C,但它们没有在 pnpm-workspace.yaml 中正确配置工作区共享,导致包 A 和包 B 都安装了包 C。

// package.json of A
{
  "dependencies": {
    "C": "^1.0.0"
  }
}
​
// package.json of B
{
  "dependencies": {
    "C": "^1.0.0"
  }
}

如果没有通过 workspace 共享,而是让每个子包独立安装依赖,C 会在每个包中重复安装。

pnpm-lock被忽略更新

pnpm-lock是什么?忽略他有什么后果?可以参考我的另外一篇文章:pnpm.lock.yaml,看似无关紧要,实则······

如何解决问题(手动)

1. 清理无用依赖

步骤:

  • 手动检查和删除无用依赖:检查每个包的 package.json,确保只保留当前实际需要的依赖。如果发现某些依赖不再使用,手动删除它们。

  • 使用工具自动检测无用依赖:可以借助一些工具自动检测项目中的无用依赖,避免遗漏。

    • depcheck:一个常用的工具,它可以帮助找出未使用的依赖和丢失的依赖。

      npm install -g depcheck
      depcheck
    • npm-check:另一个有用的工具,它不仅能检查无用依赖,还能检查过时的依赖。

      npm install -g npm-check
      npm-check

操作:

  • 删除 package.json 中未使用的依赖,并在代码中确认移除相关的导入语句。

  • 更新 pnpm-lock.yaml 文件,确保没有残留的冗余依赖。

2. 解决版本不一致

步骤:

  • 统一版本管理:确保所有子包使用相同版本的核心依赖,避免出现多个版本的冲突。如果子包依赖同一个库的不同版本,可以选择统一版本或使用 resolutions 来强制指定版本。

    // 根目录的 package.json
    {
      "resolutions": {
        "react": "^18.0.0",
        "lodash": "^4.17.21"
      }
    }
  • 检查 pnpm-lock.yaml:确保 pnpm-lock.yaml 中的依赖版本一致,手动或通过 pnpm install 重新生成 lock 文件。

操作:

  • 执行 pnpm install 重新整理依赖,确保版本一致性。

  • 使用 pnpm list 检查项目中安装的依赖树,确认是否有重复版本的依赖。

3. 优化 pnpm 的 Hoisting 设置

步骤:

  • 配置 Hoisting 规则:确保 pnpm 的工作区配置正确,避免重复依赖。可以通过调整 pnpm-workspace.yaml 中的 hoist 配置来实现依赖的提升。

    // pnpm-workspace.yaml
    packages:
      - 'packages/*'
      - 'libs/*'
    hoist:
      - 'react'
      - 'lodash'

    上面的配置会确保 reactlodash 等共享依赖被提升到根目录的 node_modules 中,避免重复安装。

  • 避免不必要的 Hoisting:虽然 Hoisting 有时能解决问题,但过度提升可能会导致某些包的版本不兼容。在某些情况下,避免将某些依赖提升到根目录可能更有利于避免版本冲突。

操作:

  • 通过 pnpm install 更新依赖,确保依赖被正确提升。

  • 使用 pnpm list --depth 3 等命令检查是否有不必要的重复安装。

4. 使用 Workspace 共享依赖

步骤:

  • 正确配置工作区依赖:确保 pnpm-workspace.yaml 中的各个包正确配置共享依赖,避免包之间的重复安装。例如,如果 A 和 B 包都依赖 C,确保通过 workspace 来共享这个依赖。

    // pnpm-workspace.yaml
    packages:
      - 'packages/*'
      - 'libs/*'
  • 集中管理依赖:将通用依赖(如 React、Lodash 等)移动到根 package.json 中,确保它们在所有包之间共享,而不是每个子包单独安装。

操作:

  • 使用 pnpm install 执行工作区依赖的统一安装,确保共享依赖被正确配置。

5. 更新 pnpm-lock 文件

步骤:

  • 重新生成 pnpm-lock.yaml 文件:在很多情况下,pnpm-lock.yaml 文件可能会被忽略更新,导致团队开发时出现版本冲突。定期清理和更新 pnpm-lock.yaml 文件,确保其中的依赖是最新且一致的。

    pnpm install --frozen-lockfile
  • 使用 pnpm audit 检查锁文件:运行 pnpm audit 可以检查项目中的依赖漏洞和潜在的安全问题,确保锁文件和依赖是最新的。

操作:

  • 删除 node_modulespnpm-lock.yaml,然后重新执行 pnpm install

  • 使用 pnpm update 更新所有依赖并确保锁文件与 package.json 一致。

6. 定期维护和监控

步骤:

  • 定期清理:定期执行 pnpm prune 命令,清理不再需要的依赖,保持依赖树干净。

    pnpm prune
  • 监控依赖:使用 pnpm outdated 定期检查过时的依赖,并及时更新。

    pnpm outdated
  • 添加自动化检测:为项目添加 CI/CD 流水线,定期运行依赖检查工具(如 depchecknpm-check),在代码提交前自动检测和清理无用依赖。

操作:

  • 在 CI 中集成依赖管理工具,定期检测和清理冗余依赖。

如何解决问题(自动化)

1. GitHub Actions CLI 示例

这个 GitHub Actions 配置脚本将帮助你自动化清理无用依赖、更新依赖版本、优化 pnpm-lock.yaml 文件等工作。

不知道什么是 GitHub Actions,可以看我另外一篇文章: github actions--最好的打工仔,帮你干脏活累活

GitHub Actions 配置文件 ci.yml
name: zhuozhuoのCI
​
#触发条件
on:
  push:
    branches:
      - dev-ywz
  pull_request:
    branches:
      - dev-ywz
#定义工作流中的任务
jobs:
  depcheck:
    runs-on: ubuntu-latest #指定工作流运行的系统
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
​
      - name: Set up pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 9.12.3
​
      - name: Install dependencies
        run: pnpm install
​
      - name: Run depcheck to find unused dependencies
        run: |
          npx depcheck --ignore-path=pnpm.lock.yaml
        continue-on-error: true
​
      - name: Report unused dependencies
        run: |
          echo "depcheck执行成功"
          if [ -f "depcheck-report.txt"]; then
            echo "auv,您这儿有没用的依赖"
            cat depcheck-report.txt
            exit 1
          fi
  version-consistency:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      
      - name: Set up pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 9.12.3
​
      - name: Check and fix version consistency
        run: |
          echo "version-consistency执行成功"
          pnpm install --prefer-offline --frozen-lockfile
          # 前面执行了强制操作,audit兜底
          pnpm audit
      
  lockfile-updata:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
​
      - name: Set up pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 9.12.3
      
      - name: Set up pnpm
        run: pnpm install
​
      - name: Clean upp unused dependencies
        run: pnpm prune
​
      - name: Update lock file
        run: |
          echo "lockile-update执行成功"
          git config --global user.email "3098448071@qq.com"
          git config --global user.name "PaiduiXiaowangzi"
          pnpm install 
          git add pnpm-lock.yaml
          git commit -m'gh自动更新了 pnpm-lock'
          git push
​
  dependencies-update:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
​
      - name: Set up pnpm
        uses: pnpm/action-setup@v2
        with: 
          version: 9.12.3
    
      - name: Install depencies
        run: |
          pnpm install 
      - name: Update outdated dependencies
        run: |
          echo "dependencies-update执行成功"
          pnpm outdated
          pnpm update
          pnpm test
​
      - name: Commit update code
        #上面成功了才执行
        if: success()
        run: |
          pnpm add .
          pnpm commit -m'gh自动更新依赖'

说明:

  1. depcheck:检查项目中的无用依赖,并报告,如果有无用依赖,CI 会失败,提醒开发者清理。

  2. version-consistency:通过 pnpm audit 检查依赖版本的一致性,并确保所有子包使用相同版本的依赖。

  3. lockfile-update:清理无用依赖并确保 pnpm-lock.yaml 文件是最新的,避免出现版本冲突。

  4. dependency-update:检查并更新所有过时的依赖,确保项目始终使用最新的依赖版本。


原文地址:https://blog.csdn.net/a3098448071/article/details/144387063

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!