手把手带你构建代码规范
手把手带你构建代码规范
代码规范构建
整合 eslint + prettier
具体如何操作请看 eslint 和 prettier 整合这一章节。
集成 husky
这里需要注意的是:
husky
本身并不提供gitHooks
的功能,他只是将git
本身的hook
的配置给抽象出来变得简单了罢了。
git hooks 是什么?
git
本身是提供hook
的,在对应的./.git/hooks
下有对应的文件的.sample
文件。
这个.sample
文件都是一些脚本,这些钩子原生都是不生效的,我们可以根据.sample
文件进行配置对应的gitHooks
,然后将后缀名.sample
去掉,这样就能生效。
这些脚本只要是可执行脚本就行,一般是shell
,python
或者perl
等。
当然对于我们前端来说一般是写shell
比较方便,只要脚本的文件名符合对应钩子即可。
对了,.git
文件夹在项目下对于windows
用户来说默认是看不见的,它是隐藏文件夹,我们需要进行如下的配置:
git hook 的作用域
对于任何git
仓库来说钩子都是本地的,而且它不会随着git clone
一起复制到新的仓库。
在开发团队中维护钩子是比较复杂的,因为.git/hooks
目录不随你的项目一起拷贝,也不受版本控制影响。
一个简单的解决办法是把你的钩子存在项目的实际目录中(.git
外),这样你就可以像其他文件一样进行版本控制。
这里也能大概看出来,对于一个团队协同多人开发的项目来说,直接用gitHooks
很麻烦。
常见 git hook
gitHooks
有很多,比如applypatch-msg", "pre-applypatch", "post-applypatch", "pre-commit", "pre-merge-commit", "prepare-commit-msg", "commit-msg", "post-commit", "pre-rebase", "post-checkout", "post-merge", "pre-push", "pre-receive", "update", "proc-receive", "post-receive", "post-update", "reference-transaction", "push-to-checkout", "pre-auto-gc", "post-rewrite", "sendemail-validate", "fsmonitor-watchman", "p4-changelist", "p4-prepare-changelist", "p4-post-changelist", "p4-pre-submit", "post-index-change"
等具体细节去看
git
官网查看:https://git-scm.com/docs/githooks这里我们只介绍两个:
pre-commit
,commit-msg
1. pre-commit
-
这个钩子由
git commit
调用,可以通过--no-verify(-n)
选项绕过; -
它不接受任何参数,并且在获取建议的提交日志消息并进行提交之前被调用;
-
该钩子对应的脚本以非零状态(
node异常退出是process.exit(1)
)退出会导致git commit
命令在创建提交之前中止; -
默认的
pre-commit
钩子在启用时,会捕获带有尾随空格的行的引入,并在找到这样的行时中止提交; -
所有
git commit
钩子都使用环境变量GIT_EDITOR
调用对应的编辑器来提交(如果该命令不会打开编辑器来修改提交消息); -
当启用
hooks.allownonascii
配置选项unset
或设置为false
时,默认的pre-commit
挂钩将阻止使用非ASCII
文件名;
2. commit-msg
-
这个钩子由
git commit
和git merge
调用,可以通过--no-verify(-n)
选项绕过; -
它接受一个参数,即保存建议提交日志消息的文件的名称(在husky中作为环境变量
$1
,早期版本是GIT_PARAMS
或者HUSKY_GIT_PARAMS
,这些变量一般情况只有在脚本中才能访问到,比如:node demo.js --param1 $1
)。而且类似这种都是局部变量,只能在commit-msg
钩子生命周期的脚本中能获取;在
git-2.31.0-windows.1
版本下,这个文件名称为:COMMIT_EDITMSG
,这个文件在.git
文件夹下,存放的是我们的commit msg
; -
以非零状态退出会导致命令中止(
node异常退出是process.exit(1)
); -
该钩子允许就地编辑消息文件,并可用于将消息规范化为某种项目标准格式(意思就是可以在这个过程中编辑
commit msg
,一般来说就是修改提交日志消息的文件)。 -
它还可用于在检查消息文件后拒绝提交;
-
默认的
commit-msg
钩子在启用时会检测重复的Signed-off-by
预告片,如果找到则中止提交;
配置 husky
由上文可以知道,如果我们自己去手动配置 git hooks 很麻烦
- 每次配置都需要去
.git
文件夹下去手动写对应的hooks
的脚本(命名要匹配对应的钩子).git
文件夹并不随着代码库一起同步如果要强行将
.git
文件夹让其和代码库一起同步的话会带来很多风险。因为我们知道,
.git
中存放的是git config
配置文件,里面有很多git
相关的配置信息,比如username
,
git config --local --list
读取的就是项目本地的git
配置文件,它的优先级很高,--global, --system
的优先级都要高,因此生效的一般都是--local
。如果你让他跟随项目文件夹一起同步的话,那么他就特别容易泄露出去。可能会造成机密信息的泄露,或者造成这个文件被篡改等的风险。
以上种种痛点如何解决呢,这里我们就引入的
husky
来协助我们配置 git hooks
这里我们选择的版本是husky: ^7.0.1
。
方法1(快捷方法):
这个(从远端调用的)命令主要做了如下 4 件事情:
-
先执行了
husky install
,然后husky install
也集成了一些操作,比如先git config --local core.hookspath .husky
,设置githooks
识别路径。然后创建了.husky
文件夹。在早期的git
版本中, 不支持修改gitHooks
识别路径, 默认只能是.git/hooks
文件夹。 -
修改
package.json
,在其中的devDependencies
加上husky
对应的版本。 -
然后设置
package.json
中的scripts
,npm set-script prepare "husky install"
,这里要注意的是npm set-script
只要在新版的npm@7.x
中才有的命令。这个
prepare
是什么呢,其实它是对应的npm
的钩子。这里的效果就是你在执行npm
命令(一般指npm install
)之前会有一个准备工作,就是执行husky install
,确保其他人也能保持正确的husky
配置。关于npm
钩子,在[npm钩子](#2.2.5 npm 钩子)中有具体描述。 -
然后新建一个钩子
npx husky add .husky/pre-commit "npm test"
上述 4 步就是npx husky-init
所完成的操作。
然后我们只需要安装在devDependencies
中添加好的husky
版本即可
方法2(手动安装配置):
具体细节在 方法1
中都有具体描述,这里就描述一下步骤即可
npm
钩子
npm
钩子只不过是一种特殊的 npm scripts --- npm life-cycle-scripts,npm pre-post-scripts因此这里可以看作
npm
钩子有两种类型:
npm
生命周期钩子;prexxx/postxxx
钩子。其中prepare
属于生命周期(life-cycle
)钩子中的一种。
prepare
的触发时机:
-
在打包之前的任何时间运行,即在
npm publish
和npm pack
期间 -
在包装打包(
packed
)之前运行 -
在包发布(
published
)之前运行 -
在本地
npm install
上运行,不带任何参数 -
在
prepublish
之后运行,但在prepublishOnly
之前运行 -
注意:如果一个通过
git
安装的包,包含一个prepare
脚本,它的dependencies
和devDependencies
将被安装,并且在包被打包(packaged
)和安装(installed
)之前准备脚本将被运行。 -
从
npm@7
开始,这些脚本在后台运行。 要查看输出,请运行:--foreground-scripts
。
对于prexxx/postxxx
钩子:
To create “pre” or “post” scripts for any scripts defined in the "scripts"
section of the package.json
, simply create another script with a matching name and add “pre” or “post” to the beginning of them.
In this example npm run compress
would execute these scripts as described.
具体细节请看npm
官网:https://docs.npmjs.com/cli/v7/using-npm/scripts
测试
我们先将改一下.husky
下的pre-commit
脚本
然后与.husky
同级新建一个scripts/test.js
然后我们执行提交命令git commit -m 'test'
, 就会有触发钩子,然后执行test.js
文件。如果有如下的输出,说明pre-commit
钩子配置成功。
这里要注意的是:
只有你手动在git bash
或者powershell
之类的终端工具上手动git commit
,才能清晰的看到输出。
如果你通过vscode或者sourcetree
之类的git
工具进行提交,对应的钩子也能触发,但是可能看不清楚一些输出。
husky 环境变量
有的是局部变量,有的是全局变量,比如
$1
,只存在commit-msg
钩子触发生命周期之内。
-
HUSKY
,如果它为"0"
,等同于--no-verify(-n)
跳过钩子。在早期版本这个变量名值为HUSKY_SKIP_HOOKS
-
$1
,这个值只有在commit-msg
时期生效,它应该是局部变量,它的值是保存commit msg
文件的名称。这个值在husky@7.x
才有,在早期版本也可能是HUSKY_GIT_PARAMS
或者GIT_PARAMS
。这个值一般需要传给校验 msg 的文件用。比如npx commitlint -E HUSKY_GIT_PARAMS
,现在就是npx commitlint -E $1
。 -
HUSKY_DEBUG
,如果它的值为"1"
,在控制台就会输出一些详细信息,比如触发了哪些钩子之类的。
这个环境变量怎么用?
我这边测试只在
git bash
生效了,不知道怎么在powershell
,node
和cmd
中用。
比如要配置HUSKY=1
的值,那么直接在git bash
下输入:
然后它就会跳过校验。
比如配置HUSKY_DEBUG=0
:
校验 commit-msg
配置好
husky
之后,我们这里只需要配置gitHooks
即可。这里其实可以应用工具 commit-lint 来校验
commit-msg
,但是为了能够让大家看的更清楚。这里我们用自己写的scripts/verifyCommitMsg.js
来看一下原理。对应的
gitHooks
为:commit-msg
新建 commit-msg 钩子
首先输入对应的命令:
这样就会在.husky
下产生新的githook: commit-msg
并且里面会有如下的内容:
这个$1
其实gitHooks
在commit-msg
期间传给husky
的参数,然后由husky
包装一下,作为环境变量放入进来。细节在常见 hook 中的 commit-msg 中有讲解。
这里的export FORCE_COLOR=1
是为了设置一下临时的环境变量FORCE_COLOR:1
,因为在husky
中默认不支持 chalk(可以美化终端字体的颜色的工具),因此我们这里要强制开启一下,FORCE_COLOR
有三种级别:1,2,3。级别由低到高支持的颜色越来越多,但兼容性就越来越低。
新建测试文件
然后与.husky
同级新建一个scripts/verifyCommitMsg.js
测试
直接在终端输入git commit -m 'hello world'
其实这里我们也能看到,先触发的钩子是pre-commit
,然后才是commit-msg
集成 lint-staged 到 husky
前面的文章讲述的都是对
commit-msg
进行的一些操作,那么如果我在commit
时,对代码质量和格式进行操作,那又该怎么办呢?这里大概的思路就是在
githook: pre-commit
中添加对应的操作。但是我们都知道,代码这个时候已经在
git staged(暂存区)
中,也就是代码改动已经git add ./
到暂存区了,这个时候如果我们手动去更改,那又会造成代码的变动。除非
git reset
退回,然后再更改,然后再git add ./
,但是这样太麻烦。那么我们有没有一种直接在
git staged(暂存区)
进行代码变动,而且不会触发反复的diff
的工具呢?这个时候我们可以选择
lint-staged
来干这件事。
在lint-staged 官网中有一句对lint-staged的描述:Run linters against staged git files and don't let 💩 slip into your code base!
。对暂存的git
文件运行linters
,不要让shi
💩一样的代码进入到你的仓库。
这里的linters
特指各种校验代码,控制代码质量等的工具。
如何配置呢?这里有两种方案:
手动配置
-
首先安装依赖:
-
然后在
package.json
中配置lint-staged
触发时需要的操作: -
最后在配置
lint-staged
触发时机,这里我们选择.husky
中的pre-commit
,也就是gitHooks
中的pre-commit
作为触发时机:
快捷配置
注意:如果你当前已经安装了
husky, lint-staged
的情况下,记得先uninstall
。
在我们已经安装了eslint, prettier
的前提下,可以直接一步到位
参考:https://prettier.io/docs/en/precommit.html#option-1-lint-stagedhttpsgithubcomokonetlint-staged
-
这行命令会帮我们安装
husky
,lint-staged
; -
并且还会执行
npx husky add .husky/pre-commit 'npx lint-staged'
,也就是在husky中新建一个钩子(pre-commit
); -
并且还会在
package.json
中写入:
mrm 是什么
命令行工具可帮助您保持开源项目的配置(
package.json、.gitignore、.eslintrc
等)同步。
优点
-
除非您愿意,否则不会覆盖您的数据;
-
最小的更改:保留原始文件格式或从
EditorConfig
读取样式; -
最小配置:尝试从项目本身或环境推断配置;
-
包括
ESLint、Prettier、lint-staged
等流行工具的可定制任务; -
用于处理
JSON、YAML、INI、Markdown
和换行符文本文件的自定义任务和工具; -
通过
npm
共享任务并将它们分组到预设中;
也就是说你可以用 mrm
来共享别人的一些工具配置的最佳实践,或者如果你自己有最佳实践也可以传上去,然后通过mrm
来同步,不管是老项目配置的升级或者新项目配置的一键配置都挺方便的。
示例
如果你想快速开始一个项目,使用单个命令安装基本 JavaScript
项目所需的一切,并在不到一分钟的时间内开始工作:
如果你想配置license, readme, contributing
文件,再次运行 mrm 以引导基本文档,并根据需要调整它们:
如果想运行一个非常老的项目,可以再次运行相同的命令以升级和迁移所有配置:
常见预设(preset
)
- ci
- codecov
- contributing
- dependabot
- editorconfig
- eslint
- gitignore
- jest
- license
- lint-staged
- package
- prettier
- readme
- semantic-release
- styleguidist
- stylelint
- travis
- typescript
快捷配置中的预设采用的就是 mrm-lint-staged。
mrm-lint-staged 是什么
Note: 现在仅支持
Prettier, ESLint, Stylelint
;最新支持请参考:mrm-lint-staged
它能做什么:
- 在
package.json
中创建一个配置; - 设置
pre-commit Git hook
; - 安装依赖项;
此任务将尝试从您的 npm
脚本推断扩展。
例如,如果你有 lint
脚本为 js
和 ts
文件运行 ESLint
,该任务将添加 lint-staged
规则,为相同的扩展运行 ESLint
。
如果您手动更改现有规则并再次运行任务,它将覆盖现有规则,但它会尝试保持您的自定义规则。
常见用法:
常见配置项(lint-staged-rules
)
可以查看 Mrm docs 和 lint-staged docs 来关注更多细节。
覆盖和自定义规则。 默认情况下,将尝试通过项目依赖项进行推断。
例如,自定义扩展:
或自定义命令:
或者您可以禁用默认规则之一:
或者添加自定义规则:
执行多个命令
集成commitizen简化提交步骤
commit msg
的书写是有规范的,但是如果每次提交我们都按照规范写,就很麻烦,这里就有一个工具来帮我们构建规范的commit msg
,那就是commitizen
。这里我们选择局部安装,是为了确保不同成员使用的都是同一个版本。
确保在不同的电脑上能有相同的行为哦。因为我们的构建代码规范都是为了让团队的不同成员能够遵守相同的代码规范,如果采用全局安装的话,不能确保每个团队成员安装的都是同一个版本。
最后在package.json
中配置脚本即可:
这里需要注意的是,在npm
的scripts
中的脚本执行时会触发钩子的,比如你这里配置commit
作为触发cz
的脚本名称,那么他就会触发scripts
名为precommits
的npm
钩子。当然,还会触发其他的生命周期钩子,这个就不细讲了。具体请看[npm钩子概述](#2.2.5 npm 钩子)。
然后你输入npm run commit
就会触发你配置的适配器的效果。
这样的们提交信息也变得简单了,并且变得很规范。
eslint和prettier整合
两者冲突的原因
eslint是用来控制代码质量的,它能处理诸如命名不规范,声明变量未使用等错误等代码质量相关的,这是它主要的作用。但是它也附带一些关于代码格式的校验功能。
prettier相比于eslint只有一个功能,那就是用来控制代码格式的(代码美观)。
当两者所要求的格式有冲突时,这个时候就会有问题。
解决方案
方案-01
既然是两者要求的格式有所冲突,因此我们可以选择直接禁用掉eslint与prettier冲突相关的规则,如果我们只是靠手写,那太麻烦了。刚好有现成的eslint-config-xxx
可以完成这个功能。
然后将其配置入eslint配置文件中,比如.eslintrc.js
,然后在extends
继承其规则,要记住,extends
中越在数组后面的规则,优先级越高。因此我们需要将要覆盖eslint规则的rule
写到后面。
如果要自定义规则:
既然我们关闭了eslint中与prettier相冲突的代码格式化相关的规则,那么我们最好就不要在rules
中再写代码格式化相关的规则,而是将代码格式化的功能直接写到prettier的配置文件中。
这样就会让eslint与prettier的规则有冲突。如果这里你设置了编辑器(比如vscode)的保存自动eslint fix
和保存自动prettier --write ./
(用prettier格式化代码),那么就会出现反复横跳的一幕。关于具体怎么集成vscode和eslint和prettier,后面细说。
方案-02
既然两者要求的格式有冲突,那么我们可不可以将prettier的格式化功能集成到eslint中来覆盖eslint自己的与prettier相冲突的代码格式化功能呢?答案自然是可以的,这里需要下载eslint-plugin-preiiter
插件。
优化写法:
其中plugin:prettier/recommended
其实是精简写法,其实我们知道,extends
其实是继承配置(可以理解为继承别的.eslintrc.js
),继承的配置如下:
注意:eslint-plugin-prettier
自己并不包含eslint-config-prettier
,因此这里需要我们本地npm install eslint-config-prettier -D
。
如果要自定义一些规则:
通过eslint可以给eslint-plugin-prettier/prettier
传递一些选项:
但是这里要注意的是,真实的eslint
能够通过usePrettierrc
识别到.prettierrc
配置文件,比如:npx eslint --fix ...
并不会出现prettier/prettier
规则错误,但是IDE
的eslint
插件却不能,因此IDE
会出现红色警告。具体可以参考:IDE eslint 插件局限性。
总结
这里采用方案-01
首先下载依赖
然后对eslint配置文件.eslintrc.js
进行配置
如果有代码格式校验上的自定义的规则(要确定这个规则是和eslint没有冲突的那部分)其实可以理解为单独是代码美观的规则,那么就在prettier配置文件.prettierrc.js
中进行配置。如果没有额外的要求,可以不写这个文件,直接采用默认即可。
如果有eslint代码质量方面规则的控制,那么就还是在eslint配置文件.eslintrc.js
的rules
进行配置。
如果需要设置eslint, prettier
忽略一些文件,可以配置对应的件,.prettierignore
,.eslintignore
,里面采用的是glob
模式。
注意点
-
如果你本地
local
有了prettier配置文件.prettierrc.js
,那么即使它内容为空。 -
大家都知道,
.editorConfig
也是控制代码格式的,但是在vscode中本来是需要插件支持才能识别这个文件。但是现在还有一个问题就是,新版的prettier它可以读取
.editorConfig
,然后将它的内容解析到自己的配置中。这就导致了即使你没有写prettier的配置文件,如果你有了
.editorConfig
,那么也就相当于你有了prettier配置文件。比如:
.editorconfig
:等同于
.prettierrc.js
:具体可以看prettier读取配置优先级
-
eslint-plugin-prettier
插件的默认读取prettier
配置文件实际对于eslint
是生效的,但是一些IDE
继承的eslint
插件不能很好的识别;
整合到 vscode
集成到vscode中,是通过安装插件的方式。
eslint 插件
为什么要安装插件?
不安装插件时我们怎么fix
代码?如果没有全局eslint,而是本地安装eslint的话。
每次我们敲完代码都需要手动去在命令行敲出这行代码,而且,我们无法实时知道我们的代码有没有问题,除非:
因此为了方便操作,我们可以将这些都交由vscode的插件来做:
这里我们用的版本时v2.1.23
,这是采用的是新版的eslint插件,因此配置也发生改变了。
首先打开vscode配置文件json
:
配置好了之后能该插件能配合编辑器实时让我们知道eslint的校验结果:
需要注意的点就是:
prettier 插件
为什么要安装插件?
不安装插件时我们怎么format
代码?如果没有全局prettier,而是本地安装prettier的话:
每次我们敲完代码都需要手动去在命令行敲出这行代码,而且,我们无法实时知道我们的代码的格式有没有问题,除非:
因此为了方便操作,我们可以将这些都交由vscode的插件来做:
当前版本为v8.1.0
首先打开vscode配置文件json
:
需要注意的是:
如果本地没有npm install preitter -D
,也没有类似于.prettierrc.js
之类的配置文件的话,并且也没有全局的prettier的话,那么vscode插件preitter
采用的是它自己捆绑的preitter
的默认配置。
读取配置优先级
NOTE: If any local configuration file is present (i.e.
.prettierrc, .editorconfig
) the VS Code settings will NOT be used.
You can use VS Code settings to configure prettier. Settings will be read from (优先级从高到低):
- Prettier configuration file;
.editorconfig
;- Visual Studio Code Settings (Ignored if any other configuration is present);
新增常见问题
Delete CR eslint(prettier/prettier)
原因:
罪魁祸首是git
的一个配置属性:core.autocrlf
。
由于历史原因,windows
下和linux
下的文本文件的换行符不一致
。
而Mac
和linux
系统,仅仅使用了换行符LF
Windows | Linux/Mac | Old Mac(pre-OSX) |
---|---|---|
CRLF | LF | CR |
‘\n\r’ | ‘\n’ | ‘\r’ |
因此,文本文件在不同系统下创建和使用时就会出现不兼容的问题。
项目仓库中默认是Linux环境
下提交的代码,文件默认是以LF结尾
的(工程化需要,统一标准)。
windows
电脑git clone
代码的时候,
如果我的autocrlf(在windows下安装git,该选项默认为true)为true
,那么文件每行会被自动转成以CRLF
结尾。
解决方案:
-
如果是
windows
系统git
全局配置之后,需要重新拉取代码。 -
通过
Eslint
-
配置prettier规则
eslint 插件和 eslint-plugin-prettier
当我们将prettier
具体规则写到单独的配置文件中,让eslint-plugin-prettier
自动读取时,
对于npx eslint --fix path
是会生效的。
但是这里有一个问题,对于一些IDE
自带的辅助插件却不会生效,比如VSCode, Webstorm
对应的eslint
插件都无法读取到,因此依旧会出现红色的错误警告。
这不是我们配置的问题,而是插件的问题。
这里有一个折衷的解决办法,就是直接在eslint
的rules
中书写prettier/prettier
规则:
这样eslint
和那些IDE
的eslint
插件都能很好的识别,但是也有一个局限性:
在eslint
中书写prettier
的规则只能将一些简单的规则写进去,对于prettier
的一些解析或者其他相关特殊的规则写进去是不会生效的,比如,当你将prettier
集成到eslint
中的rules
时,可能会出现Parsing error: Unexpected token prettier/prettier caused by "<!DOCTYPE html>"
,无法对html
进行正常的解析,这时你需要书写:
当你输入npx eslint --fix index.html
时你会发现错误已经解决,因为eslint-plugin-prettier
是会自动读取prettier
配置文件的,但是你会发现由于IDE
的eslint
插件无法识别这种情况会导致IDE
上依旧是红色警告。
参考:
https://blog.csdn.net/lty1010/article/details/124611184
https://prettier.io/docs/en/options.html#parser
但是如果你写在eslint
的里面:
你会发现eslint-plugin-prettier
,也就是eslint
根本无法识别这里的overrides
。
npx eslint --fix index.html
会报错,IDE
的eslint
插件同样会报错。
因此集成eslint, prettier
方案推荐:eslint-config-prettier
,不推荐eslint-plugin-prettier
。