一壁肋、什么是 npm 腳本?
npm 允許在package.json文件里面,使用scripts字段定義腳本命令。
{// ..."scripts":{"build":"node build.js"}}
上面代碼是package.json文件的一個片段柠并,里面的scripts字段是一個對象。它的每一個屬性富拗,對應(yīng)一段腳本堂鲤。比如,build命令對應(yīng)的腳本是node build.js媒峡。
命令行下使用npm run命令,就可以執(zhí)行這段腳本葵擎。
$ npm run build# 等同于執(zhí)行$ node build.js
這些定義在package.json里面的腳本谅阿,就稱為 npm 腳本。它的優(yōu)點很多。
1. 項目的相關(guān)腳本签餐,可以集中在一個地方寓涨。
2. 不同項目的腳本命令,只要功能相同氯檐,就可以有同樣的對外接口戒良。用戶不需要知道怎么測試你的項目,只要運行npm run test即可冠摄。
3. 可以利用 npm 提供的很多輔助功能糯崎。
查看當前項目的所有 npm 腳本命令,可以使用不帶任何參數(shù)的npm run命令河泳。
$ npm run
二沃呢、原理
npm 腳本的原理非常簡單。每當執(zhí)行npm run拆挥,就會自動新建一個 Shell薄霜,在這個 Shell 里面執(zhí)行指定的腳本命令。因此纸兔,只要是 Shell(一般是 Bash)可以運行的命令惰瓜,就可以寫在 npm 腳本里面。
比較特別的是汉矿,npm run新建的這個 Shell崎坊,會將當前目錄的node_modules/.bin子目錄加入PATH變量,執(zhí)行結(jié)束后负甸,再將PATH變量恢復(fù)原樣流强。
這意味著,當前目錄的node_modules/.bin子目錄里面的所有腳本呻待,都可以直接用腳本名調(diào)用打月,而不必加上路徑。比如蚕捉,當前項目的依賴里面有 Mocha奏篙,只要直接寫mocha test就可以了。
"test":"mocha test"
而不用寫成下面這樣迫淹。
"test":"./node_modules/.bin/mocha test"
由于 npm 腳本的唯一要求就是可以在 Shell 執(zhí)行秘通,因此它不一定是 Node 腳本,任何可執(zhí)行文件都可以寫在里面敛熬。
npm 腳本的退出碼肺稀,也遵守 Shell 腳本規(guī)則。如果退出碼不是0应民,npm 就認為這個腳本執(zhí)行失敗话原。
三夕吻、通配符
由于 npm 腳本就是 Shell 腳本,因為可以使用 Shell 通配符繁仁。
"lint":"jshint *.js""lint":"jshint **/*.js"
上面代碼中涉馅,*表示任意文件名,**表示任意一層子目錄黄虱。
如果要將通配符傳入原始命令稚矿,防止被 Shell 轉(zhuǎn)義,要將星號轉(zhuǎn)義捻浦。
"test":"tap test/\*.js"
四晤揣、傳參
向 npm 腳本傳入?yún)?shù),要使用--標明默勾。
"lint":"jshint **.js"
向上面的npm run lint命令傳入?yún)?shù)碉渡,必須寫成下面這樣。
$ npm run lint----reporter checkstyle> checkstyle.xml
也可以在package.json里面再封裝一個命令母剥。
"lint":"jshint **.js","lint:checkstyle":"npm run lint -- --reporter checkstyle > checkstyle.xml"
五滞诺、執(zhí)行順序
如果 npm 腳本里面需要執(zhí)行多個任務(wù),那么需要明確它們的執(zhí)行順序环疼。
如果是并行執(zhí)行(即同時的平行執(zhí)行)习霹,可以使用&符號。
$ npm run script1.js& npm run script2.js
如果是繼發(fā)執(zhí)行(即只有前一個任務(wù)成功炫隶,才執(zhí)行下一個任務(wù))淋叶,可以使用&&符號。
$ npm run script1.js&& npm run script2.js
這兩個符號是 Bash 的功能伪阶。此外煞檩,還可以使用 node 的任務(wù)管理模塊: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门烂。
"prebuild":"echo I run before the build script",
"build":"cross-env NODE_ENV=production webpack",
"postbuild":"echo I run after the build script"
用戶執(zhí)行npm run build的時候,會自動按照下面的順序執(zhí)行兄淫。
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 默認提供下面這些鉤子。
1.? prepublish泄私,postpublish
2.? preinstall房揭,postinstall
3.? preuninstall,postuninstall
4.? preversion晌端,postversion
5.? pretest捅暴,posttest
6.? prestop,poststop
7.? prestart咧纠,poststart
8.? prerestart蓬痒,postrestart
自定義的腳本命令也可以加上pre和post鉤子。比如漆羔,myscript這個腳本命令梧奢,也有premyscript和postmyscript鉤子。不過演痒,雙重的pre和post無效亲轨,比如prepretest和postposttest是無效的。
npm 提供一個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(不帶任何參數(shù))命令之前運行。這種行為很容易讓用戶感到困惑尚氛,所以 npm 4 引入了一個新的鉤子prepare诀诊,行為等同于prepublish,而從 npm 5 開始阅嘶,prepublish將只在npm publish命令之前運行属瓣。
八、簡寫形式
四個常用的 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是一個復(fù)合命令魂迄,實際上會執(zhí)行三個腳本命令:stop粗截、restart、start捣炬。具體的執(zhí)行順序如下熊昌。
1. prerestart
2. prestop
3. stop
4. poststop
5. restart
6. prestart
7. start
8. poststart
9. postrestart
九、變量
npm 腳本有一個非常強大的功能遥金,就是可以使用 npm 的內(nèi)部變量浴捆。
首先,通過npm_package_前綴稿械,npm 腳本可以拿到package.json里面的字段选泻。比如,下面是一個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
上面代碼中窝撵,我們通過環(huán)境變量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取到哩都。
下面是另外一個例子魁兼。
"scripts":{
????"install":"foo.js"
}
上面代碼中,npm_package_scripts_install變量的值等于foo.js漠嵌。
然后咐汞,npm 腳本還可以通過npm_config_前綴盖呼,拿到 npm 的配置變量,即npm config get xxx命令返回的值化撕。比如几晤,當前模塊的發(fā)行標簽,可以通過npm_config_tag取到侯谁。
"view":"echo $npm_config_tag",
注意锌仅,package.json里面的config對象,可以被環(huán)境變量覆蓋墙贱。
{
????"name":"foo",
????"config":{
????????"port":"8080"
????},
????"scripts":{
????????"start":"node server.js"
????}
}
上面代碼中,npm_package_config_port變量返回的是8080贱傀。這個值可以用下面的方法覆蓋惨撇。
$ npm configsetfoo:port80
最后,env命令可以列出所有環(huán)境變量府寒。
"env":"env"
十魁衙、常用腳本示例
// 刪除目錄
"clean":"rimraf dist/*",
// 本地搭建一個 HTTP 服務(wù)
"serve":"http-server -p 9090 dist/",
// 打開瀏覽器
"open:dev":"opener http://localhost:9090",
// 實時刷新
"livereload":"live-reload --port 9091 dist/",
// 構(gòu)建 HTML 文件
"build:html":"jade index.jade > dist/index.html",
// 只要 CSS 文件有變動,就重新執(zhí)行構(gòu)建
"watch:css":"watch 'npm run build:css' assets/styles/",
// 只要 HTML 文件有變動株搔,就重新執(zhí)行構(gòu)建
"watch:html":"watch 'npm run build:html' assets/html",
// 部署到 Amazon S3
"deploy:prod":"s3-cli sync ./dist/ s3: //example-com/prod-site/",
// 構(gòu)建 favicon
"build:favicon":"node scripts/favicon.js",