Kmoon Blog

区分 npm、pnpm、yarn、npx、bun

约 3.6k 字 8 min

上个月我想跑一个开源项目,在 GitHub 上搜了个 star 数挺多的 admin 后台模板,clone 下来,按照 README 写的 npm install。

结果报错,满屏的红字。

我当时就懵了。这项目不是挺火的吗,怎么跑不起来?

仔细看了报错信息,又翻了翻项目的 issues,才发现它在 package.json 旁边有一个 pnpm-lock.yaml 文件。有人说要用 pnpm install 才行。

我???

我当时就觉得,这事儿太离谱了。我学了四年计算机,居然连包管理器都没搞明白。课堂上学的是 npm,网上教程也基本都是 npm,结果现在跑个开源项目,告诉我得用 pnpm。

而且还有 yarn、bun、npx,这些东西到底有啥区别?为啥不能只用一种?

我想了想,干脆把我这段时间摸索的东西写下来。可能有些想法还不成熟,但都是我踩过坑之后的真实感受。


先说结论吧,省得你着急。

npm、pnpm、yarn,这三个都是包管理器,干的活是一样的,帮你下载和管理项目依赖的那些第三方库。bun 也是包管理器,但它野心更大,想包办你所有的 JavaScript 工具链。npx 则是个执行器,专门用来运行那些你还没安装的命令行工具。

搞定。下课。

但这样说你肯定不满意,对吧。我们一件一件来聊。


npm 是这堆东西里的老大哥。

它跟着 Node.js 一起出生,2009 年就有了。那时候 JavaScript 还只是个在浏览器里弹 alert 的小玩具,谁能想到十几年后它能统治整个互联网呢。

npm 的好处就一个,自带。你装了 Node.js,它就跟着来了,不用额外折腾。这对我们学生来说是最大的友善,毕竟课堂上的示例、网上的教程,基本都是围绕 npm 来的。

但 npm 有个臭名昭著的问题,叫「node_modules 黑洞」。

你执行 npm install 之后,项目的 node_modules 文件夹会像一个贪吃蛇一样疯狂膨胀。一个 express 项目,源码可能就几十 KB,装完依赖能给你干到几百 MB。我们宿舍那个 256GB 的 Mac,装几个项目就提示磁盘空间不足,给我整懵了。

而且 npm 早期的依赖管理有点混乱,经常出现「我电脑上能跑,你电脑上报错」这种玄学问题。你懂的,小组作业的时候最容易碰到这种情况,A 说能跑,B 说跑不了,C 说重装试试,结果重装了一晚上。

2016 年,yarn 出现了。

yarn 是 Facebook 出的,当时就是为了解决 npm 的一系列痛点。它最大的贡献是引入了 yarn.lock 文件,把依赖的版本死死钉住,再加上缓存机制和并行下载,速度快得飞起。

我在实习的时候第一次接触 yarn,那种感觉太爽了。npm install 要跑两分钟的,yarn install 几十秒搞定。

而且 yarn 有一个很贴心的设计,就是它的输出格式比 npm 好看多了。npm 安装的时候满屏乱飞的红字黄字,yarn 就干净很多,看着舒服。

但 yarn 也不是完美的。它虽然快了,但 node_modules 黑洞的问题还在。毕竟底层还是一样的机制,该占的硬盘空间一点没少。我实习公司的项目,node_modules 能到 1GB 多,每次 clone 新仓库都想哭。

这时候,pnpm 登场了。

pnpm 的思路很清奇,它说,你们这些包管理器是不是傻,同一个 lodash,十个项目要用,你们就存十份?不能搞个全局仓库,大家共用吗。

所以它搞了个叫「内容寻址存储」的东西。所有的包只存一份,项目里用硬链接或者符号链接指向那个全局仓库。这样一来,你的 node_modules 瞬间瘦身,从几百 MB 变成几 MB 都是常有的事。

我第一次看到 pnpm 的 node_modules 结构时,真的被震撼到了。文件夹里全是快捷方式,看起来跟幽灵一样,但偏偏都能跑。

而且 pnpm 还有一个很硬的优势,它严格遵守依赖树,不会出现 npm 那种「幽灵依赖」的问题。啥叫幽灵依赖,就是你明明没有在 package.json 里声明某个包,但却能在代码里引用它。这是因为 npm 的扁平化结构把依赖都摊在了一级目录下,你不知道哪个包其实是别的包的子依赖。

pnpm 的结构是严格嵌套的,你只能在声明了依赖的地方找到它,不会出现这种混乱。

说实话我现在新写的作业和项目基本都是 pnpm 了。省硬盘,速度快,结构清晰,没啥理由不用。我那个 256GB 的 Mac 终于能喘口气了。


说到这,可能有小伙伴纳闷,那 bun 又是啥?

bun 是个新玩家,2022 年才出来的,但来势汹汹。它不只是包管理器,它是一个完整的 JavaScript 运行时,对标的是 Node.js 本身。

bun 的安装速度快得离谱。它用 Zig 语言写的,号称比 npm 快 30 倍。我自己的测试,一个中等规模的课程项目,npm install 要一分多钟,bun install 十几秒完事。

而且 bun 内置了很多东西。Node.js 你要写 TypeScript 得配一堆工具,bun 直接原生支持。你要跑测试,Node.js 得装 jest 或者 vitest,bun 内置了测试 runner。你想打包,bun 也能干。

它的野心很大,就是想让你一个工具搞定所有事情。

但我目前还是观望状态。为啥呢,因为 bun 太新了,生态还没完全成熟。有些 npm 的包在 bun 上跑会有兼容性问题,特别是一些原生 C++ 扩展的模块。不是说 bun 不好,而是说如果你在做毕业设计或者期末大作业,稳定性可能还是优先考虑 npm 或者 pnpm。交作业前一天报错,那可不是闹着玩的。

不过我自己玩一些小 demo 或者刷 LeetCode 的时候,确实会用 bun。那种秒开的感觉,真的太特么赤鸡了。


好了,现在说 npx。

npx 是个异类,它不是包管理器,它是执行器

它的作用很简单,帮你运行那些你没有全局安装的命令行工具。

举个例子。你想用 create-react-app 新建一个 React 项目,传统做法是 npm install -g create-react-app,全局装一下,然后再运行。但这样一来,这个工具就常驻你电脑了,你其实只用了一次,但它一直占着茅坑。

npx 的做法是,npx create-react-app my-app,它会临时下载这个工具,执行完,然后删掉。干净,利落,不占地方。

我现在基本上所有一次性的脚手架工具都用 npx 跑。npx create-next-app,npx degit,npx ts-node,都是这个逻辑。

而且 npx 还有一个妙用。有时候你全局装了一个工具,但版本比较老,课程作业需要新版本。你可以用 npx 指定版本跑,npx create-react-app@5 my-app,这样就不会和你全局的老版本冲突。


说到这,你可能已经有点感觉了。

但这几个东西到底怎么选呢?我分享一下我自己的经验,可能有些想法还不成熟,但都是我踩过坑之后的真实感受。

如果你是刚接触编程的大一新生,先用 npm。它是标配,老师上课讲的、教材上写的、网上的入门教程,基本都是 npm。先把基础打扎实,别急着追新。

如果你开始做课程设计或者小组项目,我推荐 pnpm。node_modules 黑洞在学生电脑上真的是灾难,特别是你 C 盘只有 128GB 的情况下。pnpm 能把你从磁盘空间不足的恐惧中解救出来。而且它的严格依赖树能避免很多「我电脑上能跑」的诡异 bug,小组作业的时候少很多撕逼。

如果你喜欢尝鲜,做一些个人 side project 或者参加黑客马拉松,可以试试 bun。它的开发体验确实丝滑,内置的功能也很全,能快速搭出原型。但要有心理准备,遇到兼容性问题时可能要花时间去解决,正式交作业前还是用 npm 或 pnpm 跑一遍稳当。

yarn 呢?yarn 现在处于一个比较尴尬的位置。pnpm 出来之后,它的很多优势被蚕食了。但 yarn 有一个独特的场景,就是 Yarn Workspaces,用来做 monorepo 管理。如果你的项目是一个大型仓库,里面包含多个子项目(比如前后端在一起),yarn 的 workspace 功能还是挺成熟的。不过 pnpm 的 workspace 现在也很好用了,所以 yarn 的优势在逐渐缩小。

至于 npx,所有人都应该用起来。它和上面这些包管理器不冲突,是一个独立的工具。任何一次性的命令行操作,都用 npx,别全局安装那些你用不了几次的东西。你的电脑会感谢你的。


其实吧,这些工具之间的撕扯,背后有一个更大的逻辑。

JavaScript 的生态一直是这样,百花齐放,各显神通。没有一个「官方标准」,大家都在试,好的留下来,差的被淘汰。npm 不好用,yarn 就来了。yarn 还有问题,pnpm 就冒出来了。Node.js 太慢了,bun 就想颠覆它。

这种混乱对于学生来说确实痛苦,你要学的东西太多了,而且永远在变。你今年学的,明年可能就过时了。

但从另一个角度看,这也是 JavaScript 生态的活力所在。没有人躺在功劳簿上睡大觉,永远有人在尝试做得更好。你今天觉得 pnpm 已经够快了,明天可能就会出现一个更快的。你今天觉得 bun 已经够全了,后天可能就会有更全的。

这就是所谓的「大时代」吧,朋友们。

我们这一代人,刚好站在 JavaScript 从「浏览器脚本语言」变成「全栈通用语言」的历史节点上。我们见证了这个生态的野蛮生长,也见证了这些工具的迭代更替。多年以后回头看,你现在纠结的 npm 还是 pnpm,可能会像我们现在看「当年用不用 jQuery」一样,成为一种时代的眼泪。

但这就是技术的魅力,不是吗?

永远有新的东西值得学习,永远有人在把不可能变成可能。


最后,回到开头那个跑不起来的开源项目。

我用 pnpm install 试了一下,果然跑起来了。当时那种感觉,怎么说呢,有点爽,又有点无语。

爽的是问题解决了,无语的是,为啥这些工具不能统一一下?

但后来我明白了,这就是技术发展的常态。没有最好的工具,只有最适合的工具。

所以我的建议是,先用 npm,把基础打扎实。课堂上的东西、网上的入门教程,基本都是 npm。能跑起来最重要,等你写多了,自然会遇到 npm 解决不了的问题,那时候你就会知道 pnpm 好在哪了。

工具是服务于人的,不是来折磨人的。不要为了用新工具而用新工具,也不要因为害怕变化而拒绝新工具。

保持好奇,保持开放,但脚要踩在地上。

#JavaScript #Node.js #包管理器 #前端工具