每日壁纸

写 Python 的人,怎么理解 node、npm、pnpm、npx

Published on
/9 mins read/---

如果你平时写 Python,常常要装各种第三方 CLI 小工具,那你大概率已经接触到 node 这一套东西了。

很多好用的命令行程序、批量处理脚本、辅助工具都是用 JavaScript(或 TypeScript,都是前端语言) 写的,想跑它们就离不开 node、npm、pnpm、npx。你不用写网页,也不用了解前端框架——把它们理解成另一套用来安装、管理、运行工具的生态就够了。

一、先建立对照关系

Node 生态类比 Python实际作用
nodepython让 JavaScript 能在浏览器之外运行,所有这类工具都靠它启动
npmpip官方包管理工具,从公共仓库下载程序和库
pnpm更省心的 pipx + poetry更推荐的包管理器,占用磁盘更少,依赖更干净
npx / pnpm dlxpipx run临时运行工具,不用长期安装在本机

就像拿到一份 Python 脚本,第一件事是确认电脑上有可用的 Python 解释器;拿到一个 JS 写的命令行工具,前提就是装好 node。

二、node:让 JavaScript 跑在浏览器之外

node 本身是一个独立的执行环境,和浏览器没有必然关系。它把 JavaScript 变成一门可以在本地、服务器上直接运行的通用脚本语言,执行方式和 Python 几乎一一对应:

python demo.py
node demo.js

很多时候你不会手动去跑 .js 文件,但你调用的那些工具底层都是交给 node 去执行的。

这里最容易踩的坑是版本不匹配:不少工具对 node 版本有硬性要求,版本过低会直接报错,和拿低版本 Python 去跑有版本要求的脚本是同一个道理。如果经常尝试各类工具,最好用版本管理工具维护多个 node 版本,对应 Python 这边常用的 pyenv,具体做法可以看这篇项目 Node 版本自动切换方案。

三、npm:官方包管理器

有了运行环境之后,就需要一个渠道下载别人写好的工具,npm 就是这个官方工具管理器,对接公共的软件仓库,功能上很像 pip。

日常使用里主要就两种安装方式:一种是全局安装,装好之后在任意位置都能直接调用这个命令,近似 pip install --user;另一种是在单独目录里本地安装,只给当前目录下的脚本使用。

# 全局安装,随处可用
npm install -g tool-name
 
# 在当前目录本地安装
npm install tool-name

npm 本身能用,但用久了会发现一些麻烦:全局工具卸载后容易留下零散文件,多个目录重复安装同一个包时会反复复制,占用不少存储空间,依赖版本复杂时也容易出现一些难以排查的异常。这种体验和早年直接裸用 pip、缺少隔离的时候很像。

四、pnpm:更省空间、更干净的日常主力

也正是因为这些问题,很多人会换成 pnpm。它的命令写法和 npm 几乎一致,上手没有额外负担,但底层存储方案做了优化,很适合经常安装、测试各类工具的场景。

pnpm 会维护一份统一的内容寻址存储,所有安装过的包只保存一份原始文件,不同位置通过链接引用,不会反复复制。如果你喜欢时不时下载各种工具试手,这个特性能省下大量磁盘空间和下载时间。同时它的全局依赖目录结构更规整,卸载时残留更少。

# 全局安装,长期使用的工具优先这样装
pnpm add -g tool-name
 
# 本地安装,专供当前目录脚本使用
pnpm add tool-name

Node 自带 corepack,用 corepack enable pnpm 启用 pnpm,可以避免每次手动升级导致包管理器版本漂移。

五、npx 与 pnpm dlx:临时跑一个工具

只想简单试一个工具、不打算长期留在电脑里时,用 npx 或 pnpm 配套的 dlx。这是最常用也最容易混淆的能力,对应 Python 里的 pipx run。

它的逻辑是:如果本地没有这个工具,就自动临时拉取并运行,任务结束之后不留下常驻的全局程序,不污染你的命令环境。

# npm 自带的方式
npx tool-name
 
# pnpm 推荐的等价方式,稳定性更好
pnpm dlx tool-name

选择标准很简单:经常用到的工具就全局安装;一次性跑任务、尝鲜测试就用 dlx 或 npx。 如果某个脚本单独依赖特定版本,就放在目录里本地安装,再通过 pnpm exec 调用。

六、和 Python 生态的三个关键差别

依赖版本共存方式不同。 在 Python 里,一个虚拟环境下同一个包只能保留一个版本;node 的依赖体系天然允许同一个包的多个版本同时存在。好处是不同工具之间不容易因为依赖版本直接冲突,坏处就是偶尔会出现「明明装了某个版本,实际调用的却是另一个版本」的情况。

隔离思路不同。 Python 更习惯一个项目单独新建虚拟环境;而 node 生态里,对独立的命令行工具,大家更常用全局安装,只有配套脚本、单独的小程序才选择本地安装依赖。

不是所有包都能直接当命令用。 有些包只是提供函数库,只能被脚本引入使用;只有作者在 package.json 里声明了 bin 可执行入口的包,安装之后才会生成可以直接敲的命令。这点和 Python 包里配置 entry points 的逻辑是相通的。

收尾

整条链路串起来并不复杂:想运行一个 JS 编写的命令行工具,首先要有 node 作为运行环境;再通过 pnpm(或者 npm)把工具下载到本地;长期使用就全局安装,偶尔试用就临时拉取运行。

回头再记一遍四个词:node 是跑脚本和工具的运行环境,等价于 Python;npm 是官方的下载管理工具,能用但体验一般;pnpm 更适合作为日常主力,安装干净又节省空间;npx 和 dlx 专门用来临时跑工具,不长期占用本机环境。

理解了这一点,以后再碰到这类工具的安装指令,就不会再觉得陌生了。