2024年Stack Overflow开发者调查里,有个数据被大多数人忽略了——在"最常用工具"榜单上,包管理器的满意度分化比框架本身还剧烈。npm以67%的使用率稳居第一,但"愿意继续使用"的比例只有54%。与此同时,pnpm的使用率仅11%,推荐意愿却高达78%。

这组数字说明一件事:大多数人用着默认工具,但知道更好选择的人已经回不去了

2025年的JavaScript生态里,四个包管理器各自占据不同生态位。npm是空气,无处不在;Yarn是遗产,维护着旧帝国的边界;pnpm是效率狂的私藏;Bun则是新贵,带着运行时一起砸场子。它们都能把代码装到本地,但"怎么装"这件事,已经分化出四条完全不同的技术路线。

npm:从"能用"到"还行",花了7年

npm:从"能用"到"还行",花了7年

npm 10在2025年的表现,放在五年前会被当成科幻片。冷启动安装200个依赖,30-40秒完成。这个数字在npm 6时代是3分钟以上,而且中途崩溃的概率不低。

速度追上来了,但磁盘开销仍是硬伤。每个项目的node_modules都是独立王国,300个依赖膨胀到300-500MB是常态。十个项目就是3-5GB的重复存储,SSD用户也开始皱眉。

npm Workspaces(v7引入)解决了多包仓库的基础需求,但hoisting机制带来的"幽灵依赖"问题至今让人头疼。你明明没装某个包,却能require进来,CI环境一跑就报错。这种"本地能跑"的幻觉,调试成本往往比直接报错更高。

package-lock.json的冗长是另一道风景。一个中型项目的锁文件轻松突破5万行,代码审查时滑半天找不到有效信息。但好处是生态兼容性——每个安全扫描工具、每个CI系统都认得它,这是二十年积累的基础设施红利。

npm的真正护城河从来不是技术,而是"默认"。Node.js安装包自带,教程默认提及,新人零门槛上手。这种路径依赖构成的壁垒,比任何功能特性都更难打破。

Yarn:一场分裂造就两个物种

Yarn:一场分裂造就两个物种

Yarn的历史是技术史上最典型的"第二代困境"。2016年Facebook推出Yarn Classic,用锁文件和并行下载把npm按在地上摩擦。2020年Yarn Berry(v2+)发布,彻底重写架构,引入Plug'n'Play——然后社区裂成两半。

分裂至今没有愈合。Yarn Classic仍在维护模式接收安全补丁,但新功能冻结。大量存量项目依赖yarn.lock,迁移成本让它们困在v1。Yarn Berry的PnP机制理论上更优:包存在全局zip缓存,通过loader解析,彻底消灭node_modules黑洞。

但PnP的兼容性代价是真实的。不少原生模块、旧版工具链需要额外配置才能跑通。Yarn Berry团队做了大量兼容层,但"能跑"和"跑得顺畅"之间总有缝隙。

Yarn Berry的零安装(Zero-Installs)是另一个被低估的特性。把缓存提交到Git,CI直接跳过下载步骤。对于网络环境复杂的团队,这是救命功能。但代价是仓库体积暴涨,权衡并不总是值得。

选择Yarn的人,本质上是在选择一种工程文化:愿意接受一定复杂性,换取确定性的性能收益。这种 trade-off 在大型团队里更容易被接受——有专人维护工具链,个体开发者感知到的摩擦被组织消化了。

pnpm:磁盘效率的暴力美学

pnpm的技术路线可以用一个类比理解:npm像每个项目各自买全套工具,pnpm像社区共享工具库,用时去取。全局内容寻址存储(content-addressable store)让同一个包版本只存一份,硬链接到各项目的node_modules。

实测数据很直观。10个项目都依赖lodash@4.17.21,npm存10份,pnpm存1份。磁盘节省比例随项目规模递增,单开发者机器上省出几十GB并不夸张。CI环境的缓存效率同样提升,因为缓存键的命中率更高。

速度方面,pnpm与Yarn Berry处于同一梯队,显著快于npm。内容寻址存储的副作用是"秒级复用"——曾经下载过的包,新项目安装时几乎无感知。

pnpm的严格依赖树是另一张王牌。默认不允许访问未声明的依赖,幽灵依赖问题从源头消失。这对大型monorepo至关重要——包之间的隐式耦合是技术债的温床,pnpm的严格性相当于强制性的代码审查。

Workspace功能的设计也体现出对monorepo场景的深度理解。过滤命令(--filter)让开发者精准操作子集,任务编排(pnpm run --parallel)替代了专门的工具如lerna。很多团队砍掉了一层工具链复杂度。

React、Vue、Astro的新项目模板纷纷默认pnpm,这不是偶然。框架作者对开发者体验的敏感度,让他们更早意识到磁盘和速度的长期价值。

Bun:带着运行时一起掀桌

Bun:带着运行时一起掀桌

Bun的特殊性在于,它根本不是纯粹的包管理器。Jarred Sumner在2022年发布的这个工具,把JavaScript运行时、打包器、测试运行器、包管理器塞进同一个二进制文件。包管理只是它的四分之一功能,但单拎出来已经能打。

速度是Bun最直观的标签。用Zig语言编写,直接操作syscall,绕过Node.js的抽象层。安装依赖的速度比pnpm再快30-50%,这种差距在大型项目上会被放大到"体感明显"的级别。

但Bun的包管理器与Node.js生态的兼容性仍在追赶。2024年底的1.0版本解决了大部分阻塞性问题,边缘案例依然存在。某些postinstall脚本、原生模块的编译场景,Bun的处理方式与npm有微妙差异。

更深层的问题是锁定策略。Bun使用bun.lockb二进制锁文件,体积小巧、解析飞快,但生态工具链的支持度远不及package-lock.json和yarn.lock。安全扫描、依赖分析、SBOM生成,这些企业级流程的适配需要时间。

选择Bun的人,本质上是在押注一个更激进的未来:JavaScript工具链的重新统一。如果Bun的运行时市场份额持续增长,它的包管理器会获得天然的协同优势。但这条路的风险同样真实——技术栈的押注错误成本,在2025年仍然高昂。

2025年的选择矩阵

2025年的选择矩阵

没有 universally correct 的答案,但有清晰的决策路径。

团队规模小、项目数量少、追求零配置——npm 10已经足够。它的改进被低估了,"默认"本身就有工程价值。

维护 legacy 项目,yarn.lock 已经存在——Yarn Classic是务实选择。迁移到Berry的收益,往往不值得测试覆盖的成本。

大型monorepo、磁盘敏感、CI账单刺眼——pnpm是当前的最优解。它的设计哲学与这类场景天然契合,社区 momentum 也在向上。

愿意承担风险、追求极致速度、或者已经在用Bun运行时——bun install是合理尝试。但建议保留npm作为fallback,关键流程的兼容性验证不可跳过。

一个容易被忽略的细节:Corepack。Node.js 16.13+内置的这个工具,让切换包管理器变得无痛。corepack enable之后,项目目录下的packageManager字段自动锁定版本,团队协作的"我用yarn你用npm"问题基本消失。

2025年的包管理器战争,本质上是"默认够用"与"优化值得"两条路线的博弈。npm守住了前者,pnpm和Bun证明了后者的市场空间。Yarn的分裂则是个警示:技术迭代中的连续性断裂,代价往往由用户承担。

你的node_modules今天占了多少G?