為了方便開發者自定義,npm script 的設計者為命令的執行增加了類似生命周期的機制,具體來說就是 `pre` 和 `post` 鉤子腳本。這種特性在某些操作前需要做檢查、某些操作后需要做清理的情況下非常有用。
舉例來說,運行 npm run test 的時候,分 3 個階段:
1. 檢查 scripts 對象中是否存在 pretest 命令,如果有,先執行該命令;
2. 檢查是否有 test 命令,有的話運行 test 命令,沒有的話報錯;
3. 檢查是否存在 posttest 命令,如果有,執行 posttest 命令;
到目前為止我們所覆蓋的前端工作流包含了代碼檢查和測試自動化運行環節,**衡量測試效果的重要指標是測試覆蓋率**,而收集覆蓋率也非常的簡單,**下面逐步講解如何把代碼檢查、測試運行、覆蓋率收集這些步驟串起來**。
## 改造 test 命令
首先,我們基于鉤子機制對現有的 scripts 做以下 3 點重構,把代碼檢查和測試運行串起來:
* 增加簡單的 lint 命令,并行運行所有的 lint 子命令;
* 增加 pretest 鉤子,在其中運行 lint 命令;
* 把 test 替換為更簡單的 `mocha tests/`;
代碼 diff 如下:
```
diff --git a/package.json b/package.json
index 8f67810..d297f2e 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,17 @@
+ "lint": "npm-run-all --parallel lint:*",
"lint:js": "eslint *.js",
"lint:js:fix": "npm run lint:js -- --fix",
"lint:css": "stylelint *.less",
"lint:json": "jsonlint --quiet *.json",
"lint:markdown": "markdownlint --config .markdownlint.json *.md",
- "mocha": "mocha tests/",
- "test": "# 運行所有代碼檢查和單元測試 \n npm-run-all --parallel lint:* mocha"
+ "pretest": "npm run lint",
+ "test": "mocha tests/",
```
當我們運行 npm test 的時候,會先自動執行 pretest 里面的 lint,實際輸出如下:

## 增加覆蓋率收集
接下來,我們把運行測試和覆蓋率收集串起來,具體做法是:增加覆蓋率收集的命令,并且覆蓋率收集完畢之后自動打開 html 版本的覆蓋率報告。要實現目標,我們需要引入兩個新工具:
1. 覆蓋率收集工具 [nyc](https://github.com/istanbuljs/nyc),是覆蓋率收集工具 [istanbul](https://istanbul.js.org) 的命令行版本,istanbul 支持生成各種格式的覆蓋率報告,我已經使用多年;
2. 打開 html 文件的工具 [opn-cli](https://github.com/sindresorhus/opn-cli),是能夠打開任意程序的工具 [opn](https://github.com/sindresorhus/opn) 的命令行版本,作者是前端社區非常高產的 [Sindre Sorhus](https://github.com/sindresorhus),它在 npm 上發布了超過 1000 個包,并且質量都很不錯。
使用如下命令安裝依賴:
```
npm i nyc opn-cli -D
```
然后在 package.json 增加 nyc 的配置,告訴 nyc 該忽略哪些文件。最后是在 scripts 中新增 3 條命令:
1. precover,收集覆蓋率之前把之前的覆蓋率報告目錄清理掉;
2. cover,直接調用 nyc,讓其生成 html 格式的覆蓋率報告;
3. postcover,清理掉臨時文件,并且在瀏覽器中預覽覆蓋率報告;
具體 diff 如下:
```
diff --git a/package.json b/package.json
index 8f67810..d297f2e 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,17 @@
scripts: {
+ "precover": "rm -rf coverage",
+ "cover": "nyc --reporter=html npm test",
+ "postcover": "rm -rf .nyc_output && opn coverage/index.html"
},
@@ -22,7 +26,15 @@
"devDependencies": {
"npm-run-all": "^4.1.2",
+ "nyc": "^11.3.0",
+ "opn-cli": "^3.1.0",
"stylelint": "^8.2.0",
"stylelint-config-standard": "^17.0.0"
+ },
+ "nyc": {
+ "exclude": [
+ "**/*.spec.js",
+ ".*.js"
+ ]
}
}
```
改完之后,我們可以直接運行 npm run cover,運行的詳細截圖如下:

> **TIP#7**:到目前為止,我們的工作流中已經包含代碼檢查、測試運行、覆蓋率收集、覆蓋率查看等功能,你是不是可以用來改進下自己的工作流呢?
* * *
> 本節用到的代碼見 [GitHub](https://github.com/wangshijun/automated-workflow-with-npm-script/tree/04-pre-and-post-hooks),想邊看邊動手練習的同學可以拉下來自己改,注意切換到正確的分支 `04-pre-and-post-hooks`。
* * *
- 為什么選擇 npm script
- 入門篇 01:創建并運行 npm script 命令
- 入門篇 02:運行多個 npm script 的各種姿勢
- 入門篇 03:給 npm script 傳遞參數和添加注釋
- 進階篇 01:使用 npm script 的鉤子
- 進階篇 02:在 npm script 中使用環境變量
- 進階篇 03:實現 npm script 命令自動補全
- 高階篇 01:實現 npm script 跨平臺兼容
- 高階篇 02:把龐大的 npm script 拆到單獨文件中
- 高階篇 03:用 node.js 腳本替代復雜的 npm script
- 實戰篇 01:監聽文件變化并自動運行 npm script
- 實戰篇 02:結合 live-reload 實現自動刷新
- 實戰篇 03:在 git hooks 中運行 npm script
- 實戰篇 04:用 npm script 實現構建流水線
- 實戰篇 05:用 npm script 實現服務自動化運維