参考:
http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html
前端项目往往依赖很多打包工具、部署工具,比如webpack、grunt、gulp等等。在这些打包工具里头,有这各自的命令操作。那么面对这种情况,不同的项目不同的命令操作,我们可以将其命令封装到npm脚本(npm scripts)。Node的开发离不开npm,而 npm具有强大常用的npm 脚本。
一、什么是 npm 脚本
npm允许在package.json配置文件里面,使用scripts字段进行定义脚本命令。
{
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js",
......
},
}
在package.json文件的scripts字段,scripts字段是一个对象,它的每一个属性,对应一段脚本。比如,build字段是一个build命令,而build命令代表执行nonde build/build.js。
执行以下命令:
$ npm run build
# 等同于执行
$ node build/build.js
这些定义在package.json文件里面的脚本,称为npm 脚本。package.json 的npm 脚本具有以下优点:
- 项目的相关脚本,可以集中在一个地方。
- 不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。用户不需要知道怎么测试你的项目,只要运行
npm run test
即可。 - 可以利用 npm 提供的很多辅助功能。
查看当前项目的所有npm脚本命令,可以使用不带参数的 npm run 命令进行列举查看。
$ npm run
二、原理
npm 脚本的原理: 每当执行 npm run,就会自动新建一个Shell,在这个Shell里面执行指定的脚本命令。新建Shell前会将当前目录的node_modules/.bin子目录加入PATH变量,执行结束再恢复原样。这意为着,当前目录的node_modules/.bin子目录里面的所有脚本,都可以直接本名调用,而不必加上路径。
如:
"webpack": "webpack --config webpack.config.js --progress --colors"
不用写成下面这样:
"
webpack
": "./node_modules/.bin/webpack --config webpack.config.js --progress --colors"
三、通配符
由于npm脚本就是Shell脚本,因此可以使用Shell通配符。
"lint": "jshint *.js"
"lint": "jshint **/*.js"
上面代码中, * 表示任意文件名的js文件, ** 表示任意一层子目录。
如果要将通配符传入原始命令,防止被Shell转义,要将星号(*)转义
"test": "tap test/\*.js"
四、传参
向npm 脚本传入参数,要使用 -- 标明。
"webpack": "webpack --config webpack.config.js --progress --colors"
上面的 npm run webpack命令传入参数。
或者在命令行:
npm run webpack --config webpack.config.js --progress --colors
五、执行顺序
npm 脚本里面需要执行多个任务,那么需要明确他们执行的顺序。
如果并行执行(即同时执行),可以使用 & 符合。
$ npm run script1.js & npm run script2.js
如果继续执行(即只有前一个任务成功才执行下一个任务),可以使用 && 符合。
$ npm run script1.js && npm run script2.js
这两个符号是Bash的功能。此外,还可以使用node的任务管理模块:
script-runner、npm-run-all、redrun。
六、默认值
一般来说,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文件。
七、钩子
npm脚本有pre和post两个钩子。举例来说:build脚本命令的钩子就是prebuild和postbuild。
八、简写形式
四个常用的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脚本有一个非常强大的功能,就是可以使用npm的内部变量。
首先,通过npm_package_前缀,npm脚本可以拿到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
上面代码中,通过环境变量process.env对象,拿到package.json的字段值。如果是Bash脚本,可以用$npm_package_name和$npm_package_version取得这两个值。
npm_package_前缀也支持嵌套的package.json字段。
"repository": {
"type": "git",
"url": "xxx"
},
scripts: {
"view": "echo $npm_package_repository_type"
}
repository字段的type属性: npm_package_repository_type 取到。
注意:package.json的config对象,可被环境变量覆盖。
{
"name" : "foo",
"config" : { "port" : "8080" },
"scripts" : { "start" : "node server.js" }
}
npm_package_config_port变量返回8080。这个值可以用下面的方法覆盖:
$ npm config set foo:port 80
十、常用脚本示例
// 删除目录
"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",
// 部署到 Amazon S3
"deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/",
// 构建 favicon
"build:favicon": "node scripts/favicon.js",