前言
本文将会结合 ESLint、Prettier、husky、lint-stage 等工具使得项目一键化操作,减少在格式化、代码检查等操作上浪费时间,因为大前端真的太多东西学了,不学会“偷懒”的话,我们就要落后更多了。
本系列文章的示例 Demo 在这里 GitHub: wechat_applet_demo。
分为三篇文章介绍:
- 使Prettier一键格式化WXSS(上集)
- 使Prettier一键格式化WXSS(下集)
- 使Prettier一键格式化WXSS(结局篇)
扩展篇
- Git Commit 规范
今天介绍利用 Git Hooks 钩子实现提交代码自动执行此前的 ESLint、Prettier 命令,以保证我们提交的代码是不丑的。
正文
一、Git 钩子
Git 提供了一些钩子,能在特定的重要操作发生时触发自定义脚本。
当我们执行 git init
初始化一个 Git 版本库时,Git 会默认在 .git/hooks
目录中放置一些示例脚本(Shell 脚本)。这些示例脚本都是以 .sample
结尾,如果你想启用它们,得先移除这个后缀。
把一个正确命名(不带扩展名)且可执行的文件放入 .git/hooks
目录下,即可激活该钩子脚本。 这样一来,它就能被 Git 调用。
二、常用钩子
- pre-commit
该钩子在键入提交信息前运行。 它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。 如果该钩子以非零值退出,Git 将放弃此次提交,不过你可以用git commit --no-verify
来绕过这个环节。 你可以利用该钩子,来检查代码风格是否一致、尾随空白字符是否存在,或新方法的文档是否适当等等。
三、husky
husky 是一个为 Git 客户端增加 hook
的工具。当其安装到所在仓库时,它会自动在 .git/hooks
增加相应的钩子实现在 pre-commit
阶段就执行一系列保证每一个 commit
的正确性。
当然,pre-commit
阶段执行的命令,当然要保证其速度不要太慢,每次 commit
都等很久也不是好的体验。
1. 安装 npm-run-all
它用于同步或者并行执行 npm script 脚本。
$ yarn add --dev npm-run-all@4.1.5
于是乎,结合之前的 npm script,再通过 npm-run-all 来把几个命令串起来。
{ "scripts": { "format:all": "npm-run-all -p prettier:wxss:acss prettier:fix -s eslint:fix" } }
这行命令做了什么:首先并行执行 prettier:wxss:acss
和 prettier:fix
两个命令,等到执行完之后才会执行 eslint:fix
命令。
npm-run-all -p
表示并行操作。npm-run-all -s
表示按顺序操作。- 它同时提供了上面两条命令的简写版 API,分别对应
run-p
、run-s
。
因为 prettier:wxss:acss
和 prettier:fix
匹配的文件没有重合的,所以可以并行操作。至于为什么先进行 Prettier 格式化,再进行 ESLint 检查,因为它们两个是存在冲突的。
虽然我们可以在 .eslintrc.js
引入相关插件进行配置,使其当 Prettier 规则不符合 ESLint 规则时进行报错提醒,但没有解决我们的痛点,它需要我们手动去修复。
还有,总是可能会存在先执行 ESLint,再进行 Prettier 的情况。所以我就想着整合这个脚本,使其按照我们预期方向走:当两者有冲突的情况时,采用 ESLint 的规则。
完整脚本如下:
{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "eslint": "eslint ./ --ext .js", "eslint:fix": "eslint --fix ./ --ext .js", "prettier:fix": "prettier --config .prettierrc.js --write './**/*.{js,css,less,scss,json}'", "prettier:wxss": "gulp wxss", "prettier:acss": "gulp acss", "prettier:wxss:acss": "gulp all", "format:all": "npm-run-all -p prettier:wxss:acss prettier:fix -s eslint:fix" } }
2. 安装 husky
$ yarn add --dev husky@4.3.0
在 package.json
添加配置,使其在进行 git commit -m 'xxx'
代码提交时,进行格式化操作,以保证我们提交的代码是不丑的。
如果过程中出现错误(如 ESLint 校验不通过),将会停止 commit
操作,即 pre-commit
返回非零结果以退出。
它可以通过 git commit --no-verify
命令进行忽略。
// package.json { "husky": { "hooks": { "pre-commit": "yarn run format:all" } } }
3. 看效果
我们随便修改一个文件,然后进行提交。如图,可以看到是按照预期执行的,好了。
四、lint-staged
看到上面的结果,似乎一切顺利。但没有完...
从上图我们看出来,我们只提交了一个文件的变动,但是它对所有文件进行了扫描,这里是存在体验性问题的。
假如我们有 N 多个暂存文件,那么每当我们 git commit
一次就所有检查所有文件一遍,这导致我们的体验非常不好,过程很慢,显然不是我们想要的。
那么如何解决呢?我们需要用到它 lint-staged。
$ yarn add --dev lint-staged@10.3.0
自 v3.1 版本开始,可以有多种不同的方式进行配置,这里不多说。
在项目根目录创建一个 .lintstagedrc.js
的配置文件,然后通过 --config
或者 -c
指定。
// package.json { "husky": { "hooks": { "pre-commit": "lint-staged --config .lintstagedrc.js" } } }
// .lintstagedrc.js const path = require('path') module.exports = { '*.js': ['prettier --config .prettierrc.js --write', 'eslint --fix --ext .js'], '*.json': 'prettier --config .prettierrc.js --write', '*.wxss': absolutePaths => { // 获取相对路径 // const cwd = process.cwd() // const relativePaths = absolutePaths.map(file => path.relative(cwd, file)) // return `gulp wxss --path ${relativePaths.join(' ')}` return 'gulp wxss' }, '*.acss': 'gulp acss' }
注意,我们不将路径作为命令调用时的参数传递。这一点很重要,因为 lint-staged 将为我们完成这一点。
lint-staged
采用的是 glob
匹配模式。从上面的配置中,通过匹配不同的文件类型执行相应的操作。
*关于 lint-staged
相关使用说明,建议查看官方文档或者较瘦的这篇文章,我就不再详说。
不知道有没有人好奇,上面
lint-staged
配置文件中,我在匹配.wxss
文件时采用的是函数形式。其实这里是存在一个问题没解决的,就是在提交
.wxss
暂存文件时,不是只处理该.wxss
文件,而是将项目所有的.wxss
文件(包含未提交至暂存区的.wxss
文件)。原因大概如下:
1 在前面我介绍了,由于 Prettier 没有解析器去处理
.wxss
扩展名的文件,所以我们使用了 Gulp.js 通过转换文件类型的方式去处理。而对应 Gulp 任务是匹配当前项目下所有.wxss
文件的,使用gulp.dest(__dirname)
是正常导出到源文件路径下。2 按照
lint-staged
的思想,只处理提交的暂存文件。意味着我们在执行gulp wxss
任务时应该要传递一个文件路径,然后再修改wxssPrettier
任务,使其既能匹配所有的,也可以匹配个别或多个的(而非所有).wxss
文件。然后我尝试了很多几种方法,都没能得到预期效果。3 我踩坑思路大致是:在执行
gulp wxss
时传递一个或者多个路径参数(如上配置文件注释部分),通过process.argv
获取 NPM 脚本参数,接着在wxssPrettier
任务中对获取的参数做处理,往gulp.src()
传递一个数组,到这来我觉得思路应该是没错的。但是现实是残酷的,在gulp.dest()
时导出的路径总是不对,所有的.wxss
文件都被导出到项目根目录下了,这显然不是我们想要的结果。在 Stack Overflow 看到一个帖子,应该跟我这个问题类似。4 目前我还没找到更好的解决方案,欢迎大佬们赐教。
就是因为这个问题,我觉得我这个 wechat_applet_demo 还不是很完美(我有强迫症)
至此
到这里基本就结束了,但可能还会加入 Commit Message 提交说明的规范,因为一个清晰明了的提交说明,可以让人很清楚本次代码提交的目的或者解决了什么具体问题。目前使用最广的应该是 Angular 规范了,比较合理和系统化,而且有配套的工具。
若有不足之处,欢迎留言指正。
The end.