package.json 和旁边那个锁定文件,就能说出三件事:
哪一层把代码跑起来、哪一层把别人的代码搬进来、搬回来的包放在哪儿;再遇到"装不上、找不到模块",先分清是哪一层的账。
这三个词常被并排写进同一个标题,却站在两层:一个是把 JavaScript 跑起来的运行时,另两个是取包的包管理器。各记一行:
.js 文件。
这门语言本来只待在浏览器里,它等于给 JS 配了辆车 —— 在服务器上、在终端里,JS 都能自己跑起来。分工就此定下:运行时管"把代码跑起来",两个包管理器管"把别人的代码搬进来并记账"。三者各有各的版本,分开升级、分开出问题。
三者的麻烦同源:想跑起来的代码,一半是别人写的。这层麻烦分成两半,一半归运行时,一半归包管理器。
第 ② 条再往下拆,还藏着两个包管理器同时存在的原因:同一个包在十个项目里各拷一份,磁盘和等待时间就翻十倍。后来用"在项目里做提升、少装重复版本"缓解,又引出新的麻烦(见第 5 节)。
先分清角色,再比两个包管理器的差别。下面每一列都是能直接从项目和命令上看出来的事:
| 名词 | 它是什么 | 装依赖时磁盘怎么放 | 锁定文件 | 依赖目录长什么样 | 典型命令 |
|---|---|---|---|---|---|
| Node.js | 运行时,负责执行 JS | 不管装包这件事 | 不写 | 不建依赖目录 | node index.js |
| npm | 包管理器,随 Node 分发 | 每个项目各装一份,并把子依赖尽量提升到顶层 | package-lock.json |
扁平化的 node_modules |
npm install / npm ci |
| pnpm | 包管理器,要另外装 | 全机共用一个内容寻址仓库,项目里放硬链接 | pnpm-lock.yaml |
严格按清单铺开的 node_modules |
pnpm install |
横着读一遍:"它是什么"这一列先把运行时和包管理器分开,剩下的列说明两个包管理器的差别全在包放哪儿、装完长什么样。首次安装的速度与磁盘占用是这两条的直接后果:各装一份,账随项目数一起涨;共用一份,账几乎不随项目数变。
node index.js 启动之后,读文件、查数据库、发网络请求这些都要等,等待的时间交给操作系统,主线程转头去处理下一个任务;一条线程就能同时守着成千上万个连接。前端那一整套工具链 —— 打包器、SSG 生成器、测试框架 —— 自己也都跑在 Node 上。package.json 里分两摞:dependencies(程序跑起来要用的)与 devDependencies(只在开发和构建时用的);装完把确切版本与来源写进 package-lock.json。想按锁定文件原样重装、一个版本都不许动,用 npm ci;临时跑某个包里的一条命令,用它自带的 npx。在 IDE 或编辑器里点一下安装按钮,后台跑的就是这条命令。pnpm-workspace.yaml 声明工作区,pnpm -w 让命令作用在整个仓库、而不是某个子包上。npm install,而是 npm ci(读 package-lock.json)或 pnpm install --frozen-lockfile(读 pnpm-lock.yaml):锁定文件对不上就直接报错,不会悄悄换一个版本装上。它们不抢同一份活:要不要装包,才决定你在两个包管理器里选谁。按手头这件事挑:
package-lock.json → npm。它随 Node 自带,最省一步安装。npm ci 或 pnpm install --frozen-lockfile,别用会顺手改锁定文件的安装命令。已经站在一个项目里,最省事的判断是看项目根目录躺着哪个锁定文件:写着 package-lock.json 就跟着用 npm,写着 pnpm-lock.yaml 就跟着用 pnpm。还有一条边界要说清:包管理器只负责"把别人的代码搬进来",它不管"把你的源码做成能发布的产物" —— 后一段是构建工具的活,项目里常用 npm run build 这类脚本把两段串起来,产物再交给部署那一课去发。
三题自测,每题只有一个正确选项;同一组词换着问,按题干的动作挑。
1. 一段 JavaScript 要在服务器上跑起来,不经过浏览器;下面哪一个提供的就是这个执行环境?
对。运行时负责执行代码,它就是那个浏览器之外的执行环境;另外三个都站在"取包、装包"那一侧,代码跑起来与否不归它们管。
2. 项目根目录里躺着一个 package-lock.json,一眼就能看出这个项目装包用的是哪一个?
对。两个包管理器各写各的锁定文件,npm 写 package-lock.json、pnpm 写 pnpm-lock.yaml;锁定文件是谁写的,项目就跟着谁装。混用会把本地和流水线装到的版本悄悄拉开。
3. 同一个版本的包在十几个项目里共用,全机只存一份真身,项目里放的是指向它的硬链接 —— 说的是哪一个?
对。全机一个内容寻址仓库加硬链接是 pnpm 的做法;npm 是每个项目各装一份再做提升,所以包多起来时磁盘和安装时间都随项目数增长。