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钩子。但是,双pres和posts无效。例如,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 startnpm stop是的缩写npm run stopnpm test是的缩写npm run testnpm 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",