npx
调用项目内部安装的模块
npx主要的使用就在于去调用一些非全局安装的模块,也即是项目内部安装的模块,例如我们想在项目中使用eslint来进行代码规范检查
我们知道,如果我们通过npm i eslint -g
全局安装eslint,那么就可以在命令行中直接使用eslint ./index.js
来检查index.js
文件
而如果我们使用局部安装npm i eslint
那么我们无法在命令行中直接调用eslint ./index.js
,只能通过项目脚本和package.js的script字段来调用
script字段方式调用
这里有一个react初始化项目的package的配置文件
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
我们可以通过执行npm run lint
来调用eslint
项目脚本
在我们使用npm run 命令运行一条命令时,会自动将当前项目的 node_modules/.bin 加入系统 PATH 环境变量中,等执行结束后再将 PATH 变量恢复原样。
所以项目中 node_modules/.bin 目录里面的所有脚本,我们都是可以直接用脚本名来调用,而不必像我们正常执行一个 js 文件要 node 在加上执行文件的路径。
当我们使用 npm 或者 yarn 命令安装一个依赖包时,如果该包的 package.json 配置文件中有 bin 字段,就会自动在我们项目中的 node_modules/.bin 目录生成指向依赖包 bin 字段的软连接(symlink)执行文件。
//eslint的package.json文件
{
"name": "eslint",
"version": "8.49.0",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
"description": "An AST-based pattern checker for JavaScript.",
"bin": {
"eslint": "./bin/eslint.js"
},
"main": "./lib/api.js"
}
通过项目脚本的方式来调用eslint:node_modules/.bin/eslint ./index.js
npx方式调用
npx 的原理很简单,就是运行的时候,会到
node_modules/.bin
路径和环境变量$PATH
里面,检查命令是否存在。由于 npx 会检查环境变量
$PATH
,所以系统命令也可以调用。
npx就会帮助我们来解决这个问题,让项目内部安装的模块用起来更方便,例如使用npx,我们就可以直接使用eslint了
npx eslint ./index.js
避免全局安装模块
npx
还能避免全局安装的模块,例如想在一个没有eslint
模块的项目中执行eslint
,同时全局也没有安装eslint
,那么可以通过npx
直接执行,只要 npx 后面的模块无法在本地发现,就会下载同名模块
当npx eslint ./index.js
,代码运行时,npx
将eslint
下载到一个临时目录,使用以后再删除。以后再次执行上面的命令,会重新下载eslint
这里需要注意
npx
下载eslint
放在临时文件中,使用完之后不会立即清除,而是会过一段时间才会自动清除,下图中可以看到,短时间内第二次使用npx eslint -v
不会提示是否install,而是会直接走缓存
--no-instanll
如果想让 npx 强制使用本地模块,不下载远程模块,可以使用--no-install
参数。如果本地不存在该模块,就会报错
注意如果在之前下载过远程模块,缓存未删除之前,本地会认为存在该模块
针对这种情况我们可以手动清除缓存,使用npm config get cache
获取缓存所在的目录,删除其中的_npx
目录即可
让我们再次来测试一次,这次结果符合我们预期
--ignore-existing(目前该配置已被移除)
忽略本地的同名模块,强制安装使用远程模块,例如,本地已经全局安装了eslint
,但还是想使用远程模块,就用这个参数,让我们以下面的流程来进行测试
eslint -v
npm i eslint@7 -g
eslint -v
npx --ignore-existing eslint -v # error
-p
-p
参数用于指定 npx 所要安装的模块,后面接上需要执行的命令,对于需要安装多个模块的场景很有用
npx -p eslint eslint -v
npx -p node@16 -p eslint [command]
-c(有问题)
如果 npx 安装多个模块,默认情况下,所执行的命令之中,只有第一个可执行项会使用 npx 安装的模块,后面的可执行项还是会交给 Shell 解释,-c
参数可以将所有命令都用 npx 解释
npx -p node@16 -p eslint 'node -v | eslint -v'
npx -p node@16 -p eslint -c 'node -v | eslint -v'
不知道我理解是否有出入,在不加-c
时,esling -v
应该会被shell解释,但是全局中并无eslint模块,那么应该会报command not found: eslint
但是实际结果却是
然后我尝试清理了一下npx缓存,结果一样
我理解可能是我测试的命令会存在一些问题,我选择照着网上教程测试
这是网上教程的预期(阮大佬的教程,[链接附上](npx 使用教程 - 阮一峰的网络日志 (ruanyifeng.com)))
这是实际的运行结果
我理解,应该是新版本的npx优化了这个问题,在不加-c
的情况下也可以正确的解析
-c
参数的另一个作用,是将环境变量带入所要执行的命令。举例来说,npm 提供当前项目的一些环境变量,可以用下面的命令查看。
npm run env | grep npm_config_metrics_registry
-c
参数可以把这些 npm 的环境变量带入 npx 命令。
npx -c 'echo "$npm_config_metrics_registry"' # https://registry.npmmirror.com/
执行github源码
npx 还可以执行 GitHub 上面的模块源码。
# 执行 Gist 代码
npx https://gist.github.com/jialouluos/xxx
# 执行github
npx github:jialouluos/StudyNotes
注意,远程代码必须是一个模块,即必须包含package.json
和入口脚本(即package.json中需要存在bin)。
//package.json
"bin": {
"custom_cmd": "./index.sh"
},
指定sh文件
例如我这里有了一个模块,并且入口给出了echo
便于观察执行情况
然后执行命令
npx github:jialouluos/StudyNotes
结果如下
让我们试一下指定js文件
指定分支
再让我们来试着指定分支
分支通过#
来指定
npx github:jialouluos/StudyNotes#npx_test
结果:
多指令指定
使用-p来安装模块,并在后面指定要执行的指令
npx -p github:jialouluos/StudyNotes#npx_test custom_cmd
npx -p github:jialouluos/StudyNotes#npx_test custom_cmd2
执行结果
注:为了保持仓库整洁,主分支的
npx
我删除了,只留下npx_test
分支提供后续验证,并且不再维护npx_test
分支