你会知道有关前端包管理器的那些事

0 阅读2分钟

包管理器

背景

如今在市面上,主流的javascript包管理器主要为npm,yarn,pnpm,但这些又存在着什么样的差异性呢,让我们接下去往下看:

介绍

npm (Node Package Manager)

  • Node.js 的官方包管理器
  • 随 Node.js 一起安装

Yarn

  • 最初是为了解决 npm 的某些性能和安全问题,拥有更快的下载速度(并行下载)
  • 现在有两个主要版本: Yarn 1 (经典版)和 Yarn 2+ (Berry)

pnpm(需要Node 16+

  • 使用硬链接和符号链接来节省磁盘空间
  • 旨在解决 npm 和 yarn 的依赖重复问题(解决幽灵依赖)

版本控制符

在项目中package.json中每个依赖项总是存在一些特定的符号,如~^*

前缀/格式安装行为示例
1.2.31.2.3 (严格匹配)
~1.2.31.2.3 → 1.2.9 (仅更新补丁)
^1.2.31.2.3 → 1.9.0 (更新次版本+补丁)
^0.2.30.2.3 → 0.2.9 (0.x 表现同 ~)
1.x1.0.0 → 1.999.999

幽灵依赖

幽灵依赖是指在项目中没有声明在package.json中的包,但确能直接被使用;这些没声明但能被使用的包实际上项目依赖的间接依赖(即是依赖中的依赖)。因此,产生的核心原因是扁平化 node_modules 结构直接相关

具体实例

your-project
└── depends-on-A@1.0.0
    └──Lodash@4.17.0

在 npm/yarn 的扁平化结构下:

node_modules/
├── depends-on-A/   # 你的直接依赖
└── lodash/         # 被提升的间接依赖(幽灵依赖!)

虽然你的 package.json 中没有声明 lodash,但代码中可以这样使用:

const _ = require('lodash'); // 可以工作,但这是幽灵依赖!

pnpm相关处理

pnpm 使用不同的 node_modules 结构:

node_modules/
├── .pnpm/           # 所有依赖的实际存储位置
├── depends-on-A/    # 只包含 depends-on-A 的代码
└── lodash/          # 不存在! 因为未被显式声明

在 pnpm 中:

  • 每个包只能访问自己 package.json 中声明的依赖
  • 尝试 require 未声明的依赖会立即失败
  • 通过符号链接实现严格的依赖隔离

Peer Dependencies

Peer Dependencies(对等依赖)是 package.json 中的一种特殊依赖类型,用于声明 "我的包需要宿主环境提供这些依赖,但我不想自己安装"

如下场景:

webpack-loader需要宿主项目安装webpack,那么需要package.json声明

{
  "peerDependencies": {
    "webpack": "^5.0.0"
  }
}

总结

特性npmyarnpnpm
安装速度中等快(并行安装)最快(利用硬链接)
磁盘空间使用高(重复依赖)高(重复依赖)低(共享依赖)
锁定文件package-lock.jsonyarn.lockpnpm-lock.yaml
依赖结构扁平化(node_modules)扁平化(node_modules)嵌套+符号链接(.pnpm store)
离线模式支持(npm ci)支持(yarn --offline)默认支持
workspaces支持支持支持(优化更好)
CLI 体验基础丰富类似npm
全局存储有(.pnpm-store)