在前端的平常工做中,常常會出現「當執行一種操做以前(以後)須要同時執行另外一種操做」的狀況,好比咱們但願在每次git commit以前都運行eslint代碼檢查、npm install以前檢查項目依賴等。做爲經典的狀況,各種工具均可以讓咱們在特定的動做發生時觸發自定義腳本,這個功能就叫鉤子hooks
。javascript
平常常常用到的工具備npm
、git
、webpack
,其中的hooks
用法咱們分別介紹一下。前端
其中webpack
的hooks是webpack
爲開發者提供的運行時事件鉤子,咱們能利用它來編寫plugins
,這個就不在這裏說了,未來會單獨寫一篇關於寫plugin
的文章(立個flag╮(╯▽╰)╭)。java
目前提到npm hooks,有兩個不一樣概念的操做。node
一般意義下的監聽npm各種操做的鉤子是經過配置package.json
文件中的scripts
字段來實現的。python
而npm hooks
則是npm提供的命令行操做,目的是爲了訂閱你須要的npm package發生的特定改動,好比能夠訂閱尤大(誤)的新動態等。固然,這項功能須要你本身提供一個域名,而且須要有npm帳號且購買服務,因此就很少討論了╮(╯▽╰)╭,具體參見npm-hook官方文檔。webpack
使用方法很簡單,在項目的package.json
中的scripts
字段加入"hook": "script"
鍵值對便可。hook名稱由npm提供,script就是可以運行的shell語句。下面列幾個經常使用的hook:git
npm install
以前執行npm install
以後執行npm start
以前執行npm start
以後執行從以上的例子中能夠看出,hooks的命名是pre[op]
爲操做以前的鉤子,[op]
或post[op]
爲操做以後的鉤子。還有不少其餘的鉤子,具體能夠查閱npm script官方文檔。github
因爲歷史緣由,publish相關的鉤子會比較特殊,具體緣由和修改後的樣子都在文檔裏了,很少介紹。web
好比項目npm install
以前依賴一個全局的npm包,用戶須要先npm install -g package
,這時就能夠把該操做寫到preinstall
裏:shell
package.json
...
"scripts": {
"preinstall": "npm install -g package"
...
},
複製代碼
固然,shell的部分能夠寫全部的能夠執行的shell語句。若是須要的操做比較多,也能夠寫shell腳本,而後執行該腳本。對不少前端來講,直接開搞shell腳本比較困難,也能夠寫node、python等腳本,而後用node script.js
的方式執行也是ok的。
git hooks基本跟上面介紹的npm script hooks差很少,也是配置相應的pre-[op]
、post-[op]
之類的鉤子。
git經過項目根目錄下的.git
目錄中的內容來標記一個git庫並記錄相關信息,這點應該是衆所周知了。
git hooks的配置就在.git/hooks
目錄下,以無後綴的腳本文件的形式存在,文件名稱便是鉤子名稱,文件內容是shell腳本,你能夠自行添加可執行內容。通常在剛npm init
的hooks文件夾中全都是[hook].sample
示例文件,須要複製並更名爲該hook名稱才能夠正常使用。
根據git版本不一樣,可用的hooks也不一樣,舉下跟commit相關的hook例子:
git commit -s
那個),默認信息被建立以後運行還有不少基於其餘操做的鉤子,都因工做流程不一樣而有所不一樣,具體能夠查閱Git 鉤子官方文檔。
**鉤子腳本能夠按照指責不一樣接收不一樣的參數並進行修改。**好比commit-msg
鉤子,鉤子接收一個參數,是存有當前提交信息的臨時文件的路徑,參數能夠在shell腳本中以$1
的形式進行調用,有了這個咱們就能夠修改文件中的提交信息了。
舉個例子,當你想在每次commit以前用檢查代碼規範與否,就能夠直接在pre-commit
腳本最後添加npm run lint
(這裏看本身相關配置,不必定是這句)。
可是,git hooks的設計思路是在每臺終端以及服務器端提供不一樣的定製方案。說人話,就是由於git是一種分佈式的系統,它保證了全部終端的版本都是相同的,但hooks在服務器端和私人終端的配置可能不同,因此hooks的配置不能跟隨git提交。
例如gerrit
提供的用於修改commit message的commit-msg
鉤子就須要在git clone
的同時從遠程服務器下載到本地來替換,代碼以下:
git clone ssh://kinice@gerrit.company.com:29418/All-Projects && scp -p -P 29418 kinice@gerrit.company.com:hooks/commit-msg All-Projects/.git/hooks/
複製代碼
這固然是一種好方式。但有一種狀況,當咱們沒有其餘的能夠存儲腳本的第三方服務器,又但願將hooks同步給全部終端,該怎麼辦呢?
爲了解決上面的問題,有不少大神寫了第三方工具來實現hooks同步。對於前端來講,能夠在npm安裝的第三方工具備不少,例如husky
、yorky
、git-hooks
等。yorkie
是Vue做者尤雨溪fork了husky
並作了一些修改的工具,改善了一些使用體驗,所這裏咱們介紹一下yorkie。
*注:git-hooks
跟前兩種工具的思路不一樣,感興趣能夠了解一下:git-hooks。
yorkie
$ npm install yorkie --save-dev
複製代碼
// package.json
{
"gitHooks": {
"pre-commit": "npm test",
"commit-msg": "npm test",
"...": "..."
}
}
複製代碼
簡單到看完配置就懂了吧,直接在package.json
中增長gitHooks
這一項,並直接把想執行的shell語句寫在裏面便可。
yorkie
的實現原理在安裝過yorkie以後,比對一下安裝以前的hook文件,會發現yorkie直接重寫了全部的hooks。因此咱們把/.git/hooks/pre-commit
的核心代碼貼出來看看yorkie作了什麼:
has_hook_script () {
[ -f package.json ] && cat package.json | grep -q "\"$1\"[[:space:]]*:"
}
cd "."
# Check if pre-commit is defined, skip if not
has_hook_script pre-commit || exit 0
# Add common path where Node can be found
# Brew standard installation path /usr/local/bin
# Node standard installation path /usr/local
export PATH="$PATH:/usr/local/bin:/usr/local"
# Export Git hook params
export GIT_PARAMS="$*"
# Run hook
node "./node_modules/yorkie/src/runner.js" pre-commit || {
echo
echo "pre-commit hook failed (add --no-verify to bypass)"
exit 1
}
複製代碼
忽略上面那些檢查是否存在hook腳本的代碼,最後執行了node ./node_modules/yorkie/src/runner.js
:
const fs = require('fs')
const path = require('path')
const execa = require('execa')
const cwd = process.cwd()
const pkg = fs.readFileSync(path.join(cwd, 'package.json'))
const hooks = JSON.parse(pkg).gitHooks // 將package.json重的hooks字段取出來
if (!hooks) { // 沒有hook則退出
process.exit(0)
}
const hook = process.argv[2] // 這裏的process.argv[2]就是在hooks腳本里傳過來的hook名稱,如pre-commit
const command = hooks[hook]
if (!command) { // 不是當前hook則退出
process.exit(0)
}
console.log(` > running ${hook} hook: ${command}`)
try {
execa.shellSync(command, { stdio: 'inherit' }) // 使用execa.shellSync運行命令
} catch (e) {
process.exit(1)
}
複製代碼
關於對runner.js
的解析,我寫到了註釋中,應該都能看得懂。即經過(npm install
時改寫hooks --> 將hooks改成運行本身的runner --> runner依賴package.json
)的方式,實現了將hooks信息保存在package.json中並能夠經過git共享給全部項目成員。
俗話說,懶惰是人類進步的動力,但願能夠用這些東西,作到一鍵完成全部手工重複任務,提升咱們的工做效率,把時間用在更有意義的事情上。