npm scripts 使用指南

阮一峰發表於2016-10-11

Node 開發離不開 npm,而指令碼功能是 npm 最強大、最常用的功能之一。

本文介紹如何使用 npm 指令碼(npm scripts)。

bg2016101101

一、什麼是 npm 指令碼?

npm 允許在package.json檔案裡面,使用scripts欄位定義指令碼命令。

上面程式碼是package.json檔案的一個片段,裡面的scripts欄位是一個物件。它的每一個屬性,對應一段指令碼。比如,build命令對應的指令碼是node build.js

命令列下使用npm run命令,就可以執行這段指令碼。

這些定義在package.json裡面的指令碼,就稱為 npm 指令碼。它的優點很多。

  • 專案的相關指令碼,可以集中在一個地方。
  • 不同專案的指令碼命令,只要功能相同,就可以有同樣的對外介面。使用者不需要知道怎麼測試你的專案,只要執行npm run test即可。
  • 可以利用 npm 提供的很多輔助功能。

檢視當前專案的所有 npm 指令碼命令,可以使用不帶任何引數的npm run命令。

二、原理

npm 指令碼的原理非常簡單。每當執行npm run,就會自動新建一個 Shell,在這個 Shell 裡面執行指定的指令碼命令。因此,只要是 Shell(一般是 Bash)可以執行的命令,就可以寫在 npm 指令碼里面。

比較特別的是,npm run新建的這個 Shell,會將當前目錄的node_modules/.bin子目錄加入PATH變數,執行結束後,再將PATH變數恢復原樣。

這意味著,當前目錄的node_modules/.bin子目錄裡面的所有指令碼,都可以直接用指令碼名呼叫,而不必加上路徑。比如,當前專案的依賴裡面有 Mocha,只要直接寫mocha test就可以了。

而不用寫成下面這樣。

由於 npm 指令碼的唯一要求就是可以在 Shell 執行,因此它不一定是 Node 指令碼,任何可執行檔案都可以寫在裡面。

npm 指令碼的退出碼,也遵守 Shell 指令碼規則。如果退出碼不是0,npm 就認為這個指令碼執行失敗。

三、萬用字元

由於 npm 指令碼就是 Shell 指令碼,因為可以使用 Shell 萬用字元。

上面程式碼中,*表示任意檔名,**表示任意一層子目錄。

如果要將萬用字元傳入原始命令,防止被 Shell 轉義,要將星號轉義。

四、傳參

向 npm 指令碼傳入引數,要使用--標明。

向上面的npm run lint命令傳入引數,必須寫成下面這樣。

也可以在package.json裡面再封裝一個命令。

五、執行順序

如果 npm 指令碼里面需要執行多個任務,那麼需要明確它們的執行順序。

如果是並行執行(即同時的平行執行),可以使用&符號。

如果是繼發執行(即只有前一個任務成功,才執行下一個任務),可以使用&&符號。

這兩個符號是 Bash 的功能。此外,還可以使用 node 的任務管理模組:script-runnernpm-run-allredrun

六、預設值

一般來說,npm 指令碼由使用者提供。但是,npm 對兩個指令碼提供了預設值。也就是說,這兩個指令碼不用定義,就可以直接使用。

上面程式碼中,npm run start的預設值是node server.js,前提是專案根目錄下有server.js這個指令碼;npm run install的預設值是node-gyp rebuild,前提是專案根目錄下有binding.gyp檔案。

七、鉤子

npm 指令碼有prepost兩個鉤子。舉例來說,build指令碼命令的鉤子就是prebuildpostbuild

使用者執行npm run build的時候,會自動按照下面的順序執行。

因此,可以在這兩個鉤子裡面,完成一些準備工作和清理工作。下面是一個例子。

npm 預設提供下面這些鉤子。

  • prepublish,postpublish
  • preinstall,postinstall
  • preuninstall,postuninstall
  • preversion,postversion
  • pretest,posttest
  • prestop,poststop
  • prestart,poststart
  • prerestart,postrestart

自定義的指令碼命令也可以加上prepost鉤子。比如,myscript這個指令碼命令,也有premyscriptpostmyscript鉤子。不過,雙重的prepost無效,比如prepretestpostposttest是無效的。

npm 提供一個npm_lifecycle_event變數,返回當前正在執行的指令碼名稱,比如pretesttestposttest等等。所以,可以利用這個變數,在同一個指令碼檔案裡面,為不同的npm scripts命令編寫程式碼。請看下面的例子。

八、簡寫形式

四個常用的 npm 指令碼有簡寫形式。

  • npm startnpm run start
  • npm stopnpm run stop的簡寫
  • npm testnpm run test的簡寫
  • npm restartnpm run stop && npm run restart && npm run start的簡寫

npm startnpm stopnpm restart都比較好理解,而npm restart是一個複合命令,實際上會執行三個指令碼命令:stoprestartstart。具體的執行順序如下。

  1. prerestart
  2. prestop
  3. stop
  4. poststop
  5. restart
  6. prestart
  7. start
  8. poststart
  9. postrestart

九、變數

npm 指令碼有一個非常強大的功能,就是可以使用 npm 的內部變數。

首先,通過npm_package_字首,npm 指令碼可以拿到package.json裡面的欄位。比如,下面是一個package.json

那麼,變數npm_package_name返回foo,變數npm_package_version返回1.2.5

上面程式碼中,我們通過環境變數process.env物件,拿到package.json的欄位值。如果是 Bash 指令碼,可以用$npm_package_name$npm_package_version取到這兩個值。

npm_package_字首也支援巢狀的package.json欄位。

上面程式碼中,repository欄位的type屬性,可以通過npm_package_repository_type取到。

下面是另外一個例子。

上面程式碼中,npm_package_scripts_install變數的值等於foo.js

然後,npm 指令碼還可以通過npm_config_字首,拿到 npm 的配置變數,即npm config get xxx命令返回的值。比如,當前模組的發行標籤,可以通過npm_config_tag取到。

注意,package.json裡面的config物件,可以被環境變數覆蓋。

上面程式碼中,npm_package_config_port變數返回的是8080。這個值可以用下面的方法覆蓋。

最後,env命令可以列出所有環境變數。

十、常用指令碼示例

十一、參考連結

(完)

相關文章