Npm脚本入门教程
在使用 Node 进行开发时,您必须使用 npm,编写脚本是 npm 最强大和最常用的功能之一。我将在本文中介绍如何使用npm脚本。
1. 什么是npm脚本?
Npm允许您使用文件中的scripts
字段定义脚本命令package.json
。
{
// ...
"scripts": {
"build": "node build.js"
}
}
上面的代码是package.json
文件的一个片段,scripts
是一个 json 对象。它的每个属性都对应一个脚本。例如,与build
命令对应的脚本是node build.js
。
如果npm run
在命令行下使用该命令,则将执行该脚本。
$ npm run build
# equivalent to execution
$ node build.js
这些脚本在其中定义package.json
称为npm脚本。有许多优点:
- 与项目相关的脚本可以放在一个地方。
- 不同项目的脚本命令可以使用相同的外部接口,只要它们具有相同的功能即可。用户不需要知道如何测试项目,只需运行
npm run test
。 - 您可以利用npm提供的许多辅助功能。
您可以使用npm run
不带任何参数的命令来查看当前项目的所有npm脚本命令。
$ npm run
2. 原理
npm脚本的原理非常简单。每当npm run
执行时,将自动创建一个新shell,并在shell中执行指定的脚本命令。因此,只要命令可以由shell(通常是Bash)运行,就可以在npm脚本中编写。
特别之处在于,创建的shell npm run
会将node_modules/.bin
当前目录的子目录添加到PATH
变量中,并且PATH
在执行后将恢复该变量。
这意味着可以直接通过脚本名称调用当前目录的node_modules/bin
子目录中的所有脚本,而无需添加路径。例如,如果当前项目的依赖项中存在Mocha,则只需mocha test
直接编写:
"test": "mocha test"
而不是像这样写:
"test": "./node_modules/.bin/mocha test"
由于npm脚本的唯一要求是脚本可以在shell中执行,因此它们不一定是Node脚本,任何可执行文件都可以写在里面。
npm脚本的退出代码也遵循shell脚本的规则。如果退出代码不是0
,则npm将假定脚本无法执行。
3. 通配符
由于npm脚本是shell脚本,因此可以使用Shell通配符。
"lint": "jshint *.js"
"lint": "jshint **/*.js"
在上面的代码中,*
表示任何文件名,**
表示任何子目录。
如果要将通配符传递给原始命令以防止被shell转义,则必须转义*
。
"test": "tap test/\*.js"
4 .传递参数
使用--
指示传递给故宫脚本的参数。
"lint": "jshint **.js"
如果将参数传递给上述npm run lint
命令,则必须按如下方式编写。
$ npm run lint -- --reporter checkstyle > checkstyle.xml
您还可以再次封装命令package.json
。
"lint": "jshint **.js",
"lint:checkstyle": "npm run lint -- --reporter checkstyle > checkstyle.xml"
5. 执行命令
如果要在npm脚本中执行多个任务,则需要明确执行顺序。
如果是并行执行(同时并行执行),则可以使用&符号。
$ npm run script1.js & npm run script2.js
如果是顺序执行(仅在成功执行上一个任务时才执行下一个任务),则可以使用该&&
符号。
$ npm run script1.js && npm run script2.js
这两个符号是Bash的功能。此外,您可以使用节点的任务管理模块:script-runner
,npm-run-all
,redrun
。
6. 默认值
通常,npm脚本由用户提供。但是,npm为两个脚本提供默认值。换句话说,这两个脚本可以直接使用而无需定义。
"start": "node server.js",
"install": "node-gyp rebuild"
在上述代码中,默认值npm run start
是node server.js
,提供有一个server.js
在项目的根目录脚本; 默认值npm run install
就是node-gyp rebuild
,只要有一个binding.gyp
项目的根目录下的文件。
7. 钩子
npm脚本有两个钩子:pre
和post
。例如,build
脚本命令的钩子是prebuild
和postbuild
。
"prebuild": "echo I run before the build script",
"build": "cross-env NODE_ENV=production webpack",
"postbuild": "echo I run after the build script"
当用户执行时,它将自动按以下顺序执行npm run build
。
npm run prebuild && npm run build && npm run postbuild
因此,可以在这两个钩子中进行一些准备和清理。以下是一个例子。
"clean": "rimraf ./dist && mkdir dist",
"prebuild": "npm run clean",
"build": "cross-env NODE_ENV=production webpack"
Npm默认提供以下挂钩。
- prepublish,postpublish
- 安装前,安装
- preuninstall,postuninstall
- preversion,postversion
- 前测,后测
- 的prestop,poststop
- 起动前,启动后
- prerestart,postrestart
也可以使用pre
和post
钩子添加自定义脚本命令。例如,script命令myscript
具有premyscript
和postmyscript
钩子。但是,双pre
s和post
s无效。例如,prepretest
并且postposttest
无效。
在npm_lifecycle_event
由故宫提供的变量返回当前运行的脚本的名称,如pretest
,test
,posttest
,等。因此,您可以使用该变量为npm scripts
同一脚本文件中的不同命令编写代码。我们来看看下面的例子。
const TARGET = process.env.npm_lifecycle_event;
if (TARGET === 'test') {
console.log(`Running the test task!`);
}
if (TARGET === 'pretest') {
console.log(`Running the pretest task!`);
}
if (TARGET === 'posttest') {
console.log(`Running the posttest task!`);
}
请注意,
prepublish
钩子不仅会在npm publish
命令之前运行,而且还会在npm install
(不带任何参数)命令之前运行。但是,这样的行为很容易让用户感到困惑,所以npm 4引入了一个新的钩子prepare
,表现得像prepublish
。从npm 5开始,prepublish
钩子只会在npm publish
命令之前运行。
8. 缩写
以下是四个常用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
- 停
- poststop
- 重新开始
- 起动前
- 开始
- 启动后
- postrestart
9. 变量
npm脚本具有非常强大的功能,允许您使用npm的内部变量。
首先,npm脚本可以获取package.json
带有npm_package_
前缀的字段。例如,这是package.json
下面的内容。
{
"name": "foo",
"version": "1.2.5",
"scripts": {
"view": "node view.js"
}
}
然后,变量npm_package_name
返回foo
,变量npm_package_version
返回1.2.5
。
// view.js
console.log(process.env.npm_package_name); // foo
console.log(process.env.npm_package_version); // 1.2.5
在上面的代码中,我们package.json
通过环境变量process.env
对象获取字段值。如果它是Bash脚本,您可以使用$npm_package_name
和获取两个值$npm_package_version
。
该npm_package_
前缀也支持嵌套的package.json
领域。
"repository": {
"type": "git",
"url": "xxx"
},
scripts: {
"view": "echo $npm_package_repository_type"
}
在上面的代码中,您可以获取type
该repository
字段的属性npm_package_repository_type
。
这是另一个例子。
"scripts": {
"install": "foo.js"
}
在上面的代码中,npm_package_scripts_install
变量的值等于foo.js
。
npm的配置变量(npm config get xxx
命令返回的值)也可以通过npm_config_
前缀获得。例如,您可以使用获取当前模块的release标记npm_config_tag
。
"view": "echo $npm_config_tag",
请注意,环境变量可以覆盖该config
对象package.json
。
{
"name" : "foo",
"config" : { "port" : "8080" },
"scripts" : { "start" : "node server.js" }
}
在上面的代码中,npm_package_config_port
变量返回8080
。并且可以通过以下方法覆盖此值。
$ npm config set foo:port 80
env命令可以列出所有环境变量。
"env": "env"
10. 常见脚本示例
// 删除目录
"clean": "rimraf dist/*",
// 生成本地http服务
"serve": "http-server -p 9090 dist/",
// 打开浏览器
"open:dev": "opener http://localhost:9090",
//实时刷新
"livereload": "live-reload --port 9091 dist/",
// 生成html文件
"build:html": "jade index.jade > dist/index.html",
// 只要CSS文件已更改,就重新执行生成。
"watch:css": "watch 'npm run build:css' assets/styles/",
// 只要HTML文件已更改,就重新执行生成
"watch:html": "watch 'npm run build:html' assets/html",
// 部署到AmazonS3。
"deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/",
// 生成 favicon
"build:favicon": "node scripts/favicon.js",