在前端的平常工做中,常常会出现“当执行一种操做以前(以后)须要同时执行另外一种操做”的状况,好比咱们但愿在每次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共享给全部项目成员。
俗话说,懒惰是人类进步的动力,但愿能够用这些东西,作到一键完成全部手工重复任务,提升咱们的工做效率,把时间用在更有意义的事情上。