作为开发者,我们每天都在和 npm scripts
打交道。它就像一个自动化助理,帮我们处理编译、测试、部署等各种繁琐任务。但在使用过程中,你是否对如何向脚本传递参数感到困惑?
特别是,你可能见过下面这两种看似相同却又略有不同的命令:
bash
npm test tests/contract/auth-github-callback.test.ts
npm test -- tests/contract/auth-github-callback.test.ts
这两种方式似乎都能正常工作,那么它们之间到底有什么区别?那个神秘的 --
究竟扮演了什么角色?今天,我们就来深入探讨这个话题。
npm 脚本如何工作
首先,我们来看一下 package.json
文件中 scripts
的定义。在我们的项目中,test
命令是这样配置的:
json
"scripts": {
"test": "jest",
...
}
这意味着,当你运行 npm test
时,npm 实际上是在执行 jest
命令。同理,npm run dev
就是在执行 next dev
。
参数传递的两种方式
当我们想给 jest
命令传递参数时(例如,指定只运行某一个测试文件),我们自然会把参数跟在 npm test
后面。这就引出了我们开头的问题:带 --
和不带 --
有何不同?
不带 --
:npm 的"猜测"
当你运行 npm test tests/contract/auth-github-callback.test.ts
时,npm 会接收到 test
这个脚本名和 tests/contract/...
这个参数。它会启动 jest
进程,然后把这个参数传递给它。在大多数情况下,jest
能够正确解析这个参数,并执行你指定的测试文件。
带 --
:明确的"指令"
当你运行 npm test -- tests/contract/auth-github-callback.test.ts
时,情况就有所不同。双破折号 --
在 npm 中是一个特殊的标志,它告诉 npm 的命令行解析器:"到此为止,我(npm)的参数解析结束了。后面所有的内容,都原封不动地、直接地传递给底层执行的脚本。"
换句话SHUO,--
就像一个"参数分隔符",明确地将 npm
自身的参数和要执行脚本的参数分离开来。
可视化参数传递流程
我们可以使用序列图来更清晰地展示这个过程。
场景一:不使用 --

在这种情况下,npm 接收到参数,并将其传递给 jest。
场景二:使用 --

使用 --
后,npm 明确知道 my-test.ts
不属于自己,而是属于 jest 的。
为什么有时不带 --
也能工作?
既然 --
如此重要,为什么在很多情况下,不加它命令也能成功执行呢?
这得益于 npm 和大多数命令行工具(如 jest
)的"宽容"。当 npm 遇到它不认识的参数时(比如 tests/contract/...
),它会猜测这可能是你想传递给底层脚本的参数,于是就"顺便"帮你传过去了。jest
也足够智能,能够正确识别并处理这个参数。
何时必须使用 --
?
然而,这种"猜测"和"宽容"并非总是可靠。当你要传递的参数与 npm 自身的某个参数冲突时,问题就出现了。
假设你有一个脚本,它接受一个名为 -v
的参数来输出版本号。同时,npm -v
是一个有效的 npm 命令,用于显示 npm 自身的版本。
- 如果你运行
npm run your-script -v
:npm 会把它解析成npm run your-script -v
,然后执行npm -v
,最终输出 npm 的版本号,而不是把-v
传递给你的脚本。 - 如果你运行
npm run your-script -- -v
:--
会告诉 npm 不要理会-v
,把它原封不动地交给your-script
。这样,你的脚本就能正确接收到-v
参数,并执行预期的操作。
结论与最佳实践
虽然在很多情况下,省略 --
也能正常工作,但我们强烈建议你养成始终使用它的习惯。
- 明确性 :
--
清晰地表明了你的意图,让命令更容易阅读和理解。任何看到这个命令的人都能立刻明白,--
之后的所有内容都是为底层脚本准备的。 - 安全性:它能完全避免参数与 npm 自身参数发生冲突的风险,让你的脚本在任何情况下都能稳定、可预测地运行。
- 遵循规范 :使用
--
来分隔参数是 POSIX 规范的一部分,也是命令行工具中一个广泛遵循的约定。遵循这个规范能让你的技能在更广泛的工具和平台中通用。
总而言之,下次当你需要向 npm 脚本传递参数时,请毫不犹豫地使用 --
。这是一个简单却能带来巨大好处的最佳实践。