了解 npx 的真实工作原理
Source: Dev.to
摘要
本文从两个层面解释 npx:
- 简要概览以及精确的解析步骤
- 对每一步的深入解释
概览
npx 会搜索可执行文件并运行它。
如果本地或全局都没有该可执行文件,它会临时下载并运行。
把 npx 看作 可执行文件解析器,而不是包管理器。
精确的解析顺序(快速浏览)
What npx does?
Searches for a file and executes it.
Search Step-1
Searches for package.json in current working directory
Searches for name key in the json
Searches for bin key
Search Step-2
Searches for node_modules/.bin/hello in current working directory
And executes this file
Search Step-3
Searches for hello in global npm folder
And executes this file
Search Step-4
Searches for hello package in npx cache
And executes this file
Search Step-5
Searches for hello package in npm registry
Prompts to install the package if found
Downloads and installs
一旦找到匹配项,执行会立即停止。
深入解释:每一步是如何工作的
步骤 1:项目 package.json — 项目范围的 CLI
npx 会检查当前目录是否包含 package.json,并检查其中的 name 与 bin 字段。
{
"name": "hello",
"bin": {
"hello": "./index.js"
}
}
如果命令名与 name 相同且 bin 字段暴露了同名命令,npx 会直接运行映射的文件。
为什么重要
这使得 项目级别的 CLI 能够在不需要全局安装或依赖全局工具的情况下使用。
步骤 2:本地 node_modules/.bin(首选执行方式)
如果步骤 1 失败,npx 会在本地二进制文件夹中查找可执行文件:
./node_modules/.bin/hello
该文件夹会在 npm 安装声明了 bin 字段的依赖时自动创建。
npm install hello-cli # creates node_modules/.bin/hello
npx hello # executes that file
关键规则
本地项目的可执行文件始终会覆盖全局的同名文件。
步骤 3:全局 npm 二进制文件夹
如果没有本地可执行文件,npx 会检查全局 npm 二进制目录:
npm bin -g # e.g., /usr/local/bin
如果在该目录下存在 hello,npx 会运行它。
优先级较低的原因
全局 CLI 可能导致不同项目之间的版本不匹配;npx 更倾向使用本地工具以避免此类问题。
步骤 4:npx 缓存 — 快速复用
如果仍未找到,npx 会回退到内部缓存,该缓存:
- 保存之前下载过的 CLI 包
- 防止重复下载
- 加速重复任务的执行
缓存由 npm 管理,并与 npm 的内部机制共享。
步骤 5:npm 注册表(最后手段)
当所有前面的步骤都失败时,npx 会查询 npm 注册表。如果存在名为 hello 的包:
- 下载该包。
- 解析其
bin条目。 - 立即运行可执行文件。
- 将该包缓存以供后续使用。
示例:
npx create-react-app my-app
不会进行全局安装,也不会写入 package.json。
记忆模型
npx = 解析可执行文件,而不是安装依赖。
只有在解析失败时才会进行安装。
最终要点
npx 是一个 命令运行器:
- 本地项目工具始终优先
- 全局工具是后备
- 缓存避免重复下载
- 注册表获取是最后一步
在以下场景使用 npx:
- 一次性使用 CLI
- 零全局安装
- 版本安全的工具链
- 干净的开发者环境
思考题
Q1: 如果未指定版本,npx 如何决定下载哪个版本?
Q2: 如果同一个命令在本地和全局都有,怎么办?
Q3: 为什么在 npx 解析过程中 node_modules/.bin 会优先于全局二进制文件?