Npm Script
本文将介绍如何使用 npm 脚本(npm scripts)。
介绍
NPM 脚本是 package.json 中定义的一组内置脚本和自定义脚本。他们的目标是提供一种简单的方法来执行重复的任务,比如:
- 启动项目
- 打包项目
- 执行单元测试,生成测试报告之类
- ……
那如何定义一个 NPM 脚本?需要做的就是设置它的名称,并在 package.json 文件的 script 属性中编写该脚本, 如下:
1 | { |
上面代码是package.json
文件的一个片段,里面的scripts
字段是一个对象。它的每一个属性,对应一段脚本。比如,build
命令对应的脚本是node build.js
。
命令行下使用npm run
命令,就可以执行这段脚本。
1 | $ npm run build |
这些定义在package.json
里面的脚本,就称为 npm 脚本。它的优点有很多。
查看当前项目的所有 npm 脚本命令,可以使用不带任何参数的npm run
命令。
1 | $ npm run |
原理
npm 脚本的原理非常简单。每当执行npm run
,就会自动新建一个 Shell,在这个 Shell 里面执行指定的脚本命令。因此,只要是 Shell(一般是 Bash)可以运行的命令,就可以写在 npm 脚本里面。
比较特别的是,npm run
新建的这个 Shell,会将当前目录的node_modules/.bin
子目录加入PATH
变量,执行结束后,再将PATH
变量恢复原样。
这意味着,当前目录的node_modules/.bin
子目录里面的所有脚本,都可以直接用脚本名调用,而不必加上路径。
比如:
1 | { |
由于 npm 脚本的唯一要求就是可以在 Shell 执行,因此它不一定是 Node 脚本,任何可执行文件都可以写在里面。
npm 脚本的退出码,也遵守 Shell 脚本规则。如果退出码不是0
,npm 就认为这个脚本执行失败。
传参
向 npm 脚本传入参数,要使用--
标明。
例如向上面的npm run lint
命令传入参数--cache
,必须写成下面这样。
1 | $ npm run lint -- --cache |
运行如下:
1 | $ npm run lint -- --cache |
执行顺序
如果 npm 脚本里面需要执行多个任务,那么需要明确它们的执行顺序。
- 如果是继发执行(即只有前一个任务成功,才执行下一个任务),可以使用
&&
符号。
1 | $ npm run lint && npm run build |
- 如果是并行执行(即同时的平行执行),可以使用
&
符号。
1 | $ npm run lint & npm run build |
这两个符号是 Bash 的功能。此外,还可以使用 node 的任务管理模块:script-runner、npm-run-all、redrun。
钩子 pre & post
npm 脚本有pre
和post
两个钩子,我们可以为任何脚本创建 pre
和 post
钩子,NPM 会自动按顺序运行它们。唯一的要求是脚本的名称(后跟pre
或post
前缀)与主脚本匹配。例如:
需要注意:双重的
pre
和post
无效,比如prepretest
和postposttest
是无效的。
1 | { |
如果我们执行 npm run hello,npm 会按以下顺序执行脚本: prehello, hello, posthello。输出如下:
1 | $ npm run hello |
npm 默认提供下面这些钩子。
- prepublish,postpublish
- preinstall,postinstall
- preuninstall,postuninstall
- preversion,postversion
- pretest,posttest
- prestop,poststop
- prestart,poststart
- prerestart,postrestart
注意,从npm@1.1.71开始
prepublish
这个钩子不仅会在npm publish
命令之前运行,还会在npm install
(不带任何参数)命令之前运行,这种行为很容易让用户感到困惑,所以 npm 4 引入了一个新的钩子prepare
,行为等同于prepublish
,来替代上述功能,另外一种新钩子prepublishOnly
作为替代策略,让用户避免以往 npm 版本的混乱行为,prepublishOnly
只在npm publish
前执行,而从 npm 5 开始,prepublish
将只在npm publish
命令之前运行,即取代prepublishOnly
,所以 npm6 即以后的版本会放弃prepublishOnly
。
prepare
: 在两种情况前运行,一是npm publish
命令前,二是不带参数的npm install
命令;它会在prepublish
之后、prepublishOnly
之前执行prepublishOnly
: 在npm publish
命令前执行
npm 提供一个npm_lifecycle_event
变量,返回当前正在运行的脚本名称,比如pretest
、test
、posttest
等等。所以,可以利用这个变量,在同一个脚本文件里面,为不同的npm scripts
命令编写代码。请看下面的例子。
1 | { |
1 | const TARGET = process.env.npm_lifecycle_event; |
运行如下:
1 | $ npm run test |
简写形式
四个常用的 npm 脚本有简写形式。
npm start
是npm run start
npm stop
是npm run stop
的简写npm test
是npm run test
的简写npm restart
是npm run stop && npm run restart && npm run start
的简写
npm start
、npm stop
和npm restart
都比较好理解,而npm restart
是一个复合命令,实际上会执行三个脚本命令:stop
、restart
、start
。具体的执行顺序如下。
- prerestart
- prestop
- stop
- poststop
- restart
- prestart
- start
- poststart
- postrestart
变量
在执行 NPM 脚本时,NPM 提供了一组我们可以使用的环境变量。 我们可以使用
1 | npm_config_<val> 或者 npm_package_<val> |
- 通过
npm_config_
前缀,拿到 npm 的配置变量,即npm config get xxx
命令返回的值 - 通过
npm_package_
前缀,npm 脚本可以拿到package.json
里面的字段。 env
命令可以列出所有环境变量
例如:
1 | { |
1 | // index.js |
上面代码中,我们通过环境变量process.env
对象,拿到package.json
的字段值。如果是 Bash 脚本,可以用$npm_package_name
和$npm_package_version
取到这两个值。
npm_package_
前缀也支持嵌套的package.json
字段。
1 | { |
运行如下:
1 | $ npm run echo:test |
最后,env
命令可以列出所有环境变量。
1 | { |
命名规则
前缀
有些开源项目我们可以看到,他的 script 脚本是带有前缀的, 这也是一个好的命名规则, 通过:dev
, :prod
来区分环境, 如下:
1 | { |
更多详情请查看官网 npm scripts