前端工程化-代码提交规范 | 公司项目

分析和结论

原公司项目禁用了eslint对.js,.vue文件的eslint,所以使用lint-staged的意义不大。

image.png

主要可以做的工作在于commit提交规范;

大致步骤:

1 安装husky, 初始化 husky, 会在根目录创建 .husky 文件夹;

2 安装commitlint @commitlint/config-conventional,执行.husky/commit-msg初始化命令,并且需要配置commitlint.config.js;

3 此时,git commit 已经可以出发commitlint的相关规则了;

4 进一步做好命令行的提示,安装commitizen cz-conventional-changelog ,添加npm set-script commit “git-cz”脚本命令,初始化编写commit指令(npx commitizen init cz-conventional-changelog –save-dev –save-exact)

5 此时,命令行有提示流程了,根据相关rules进行提交commit了;

6 进一步自由度的定制化,则可以安装commitlint-config-cz,指定配置prompt文件。

tips:

如果使用webstorm,它有个插件叫git-commit-template可以界面化来引导你按规范提交代码。

如果开发工具是VSCode,就只能用commitizen这个工具在命令行进行提交了。

具体完整步骤

格式:Angular提交规范

Angular提交规范的格式很简单,基本尝试一次就能学会。若再有命令行工具的加持,可一直用一直爽!
Angular提交规范的格式包括Header、Body和Footer三个内容。Header为必填项,Body与Footer为可缺省项,这些内容通过以下结构组成一个完整的提交格式。

1
2
3
4
5
<type>(<scope>): <subject>
# 空一行
<body>
# 空一行
<footer>

该部分仅书写一行,包括三个字段,分别是type、scope和subject。

  • type:用于说明commit的提交类型,必选
  • scope:用于说明commit的影响范围,可选
  • subject:用于说明commit的细节描述,可选

type用于说明commit的提交类型,包括以下选项,相信这些选项已满足日常95%的应用场景。当然这些选项无需刻意记忆,我会引入命令自动完成这些提交工作。

类型 功能 描述
feat 功能 新增功能,迭代项目需求
fix 修复 修复缺陷,修复上一版本存在问题
docs 文档 更新文档,仅修改文档不修改代码
style 样式 变动格式,不影响代码逻辑
refactor 重构 重构代码,非新增功能也非修复缺陷
perf 性能 优化性能,提高代码执行性能
test 测试 新增测试,追加测试用例验证代码
build 构建 更新构建,改动构建工具或外部依赖
ci 脚本 更新脚本,改动CI或执行脚本配置
chore 事务 变动事务,改动其他不影响代码的事务
revert 回滚 回滚版本,撤销某次代码提交
merge 合并 合并分支,合并分支代码到其他分支
sync 同步 同步分支,同步分支代码到其他分支
impr 改进 改进功能,升级当前功能模块

scope用于说明commit的影响范围。简要说明本次改动的影响范围,例如根据功能可划分为数据层、视图层和控制层,根据交互可划分为组件、布局、流程、视图和页面。从Angular以往的提交说明来看,还是建议你在提交时对scope补全。
subject用于说明commit的细节描述。文字一定要精简精炼,无需太多备注,因为Body部分可备注更多细节,同时尽量遵循以下规则。

  • 以动词开头
  • 使用第一人称现在时
  • 首个字母不能大写
  • 结尾不能存在句号(.)

例如本次提交说明的subject的中文是改变按钮的颜色,根据上述规则转换为英文就是change the color of the button。
理解好Header三个字段各自含义,每次提交说明通过上述规范的约束就变成以下模样。

1
2
feat(View): 新增主题皮肤切换按钮
feat(View): new the button for theme skin switching
Body

该部分可书写多行,对subject做更详尽的描述,内容应包括改动动机与改动前后对比。

该部分只适用两种情况,分别是不兼容变动与问题关闭。

  • 不兼容变动:当前代码与上一版本不兼容,则以BREAKING CHANGE开头,关联变动描述、变动理由和迁移方法
  • 问题关闭:当前代码已修复某些Issue,则以Closes开头,关联目标Issue

方案:部署Git的提交格式化

理解Angular提交规范的格式后,就能在每次提交时书写标准的提交说明了,同时使用专业工具做专业事情,能输出更高质量的提交说明。接着介绍两个专业工具该如何配置与部署。

commitizen

每次执行git commit命令时,需根据上述规范整理提交说明的格式,但提交说明本身并不是项目开发的必须项, 所以可巧借工具完成提交规范。
因为当前目标还要通过工具生成与约束提交说明,而commitizen是一个基于模板驱动的约束规范工具,可扩展性很强,因此推荐使用commitizen
使用commitizen的git cz命令可代替原生的git commit命令,帮助开发者生成符合规范的提交说明。在此还需指定一个符合Angular提交规范的书写配置cz-conventional-changelog,使得commitizen根据指定规范帮助开发者生成提交说明。
commitizen与cz-conventional-changelog可全局部署也可局部部署,如何选择只能根据实际情况了。
全局部署
全局安装commitizen与cz-conventional-changelog。

1
npm i -g commitizen cz-conventional-changelog

不同系统中创建.czrc文件,具体情况如下,加入以下内容。

  • Windows系统:在C:/Users/$USER目录中创建.czrc文件
  • MacOS系统:在~目录中创建.czrc文件
1
{ "path": "cz-conventional-changelog" }

局部部署
局部安装commitizen与cz-conventional-changelog。

1
npm i -D commitizen cz-conventional-changelog

在package.json中指定scripts与config。

1
2
3
4
5
6
7
8
9
10
{
"script": {
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
}
}
}

使用上述两种方式配置好commitizen,就能使用git cz或npm run commit代替git commit了,依次完成所有步骤就能规范地提交了。

自定义规范
可能Angular提交规范的某些情况不适合当前需求,可通过cz-customizable制定一份符合自己团队的提交规范。
基于上述全局部署与局部部署,以下配置也有些许不同。
对于全局部署,全局安装cz-customizable,在.czrc中重新指定path。

1
npm i -g cz-customizable
1
{ "path": "cz-customizable" }

对于局部部署,局部安装cz-customizable,在package.json中重新指定config。

1
2
bash
复制代码npm i -D cz-customizable
1
2
3
4
5
6
7
8
json
复制代码{
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
}
}
}

不同系统中创建.cz-config.js文件,具体情况如下,制定一份符合自己团队的提交规范。为了方便理解,我将原来英文版的commit type改成中文版的commit type并增加几个type。

  • 在Windows系统全局部署:在C:/Users/$USER目录中创建.cz-config.js文件
  • 在MacOS系统中全局部署:再~目录中创建.cz-config.js文件
  • 在项目中局部部署:在根目录中创建.cz-config.js文件

commitlint

除了规范地书写提交说明,还需借助commitlint规范地校验提交说明。commitlint的校验标准较严格,只要不符合规范的提交说明就直接拒绝。
同commitizen一样,commitlint也需一套校验配置。当然还是推荐与Angular提交规范相关校验配置@commitlint/config-conventional。然而我暂时还未能实践出commitlint的全局部署,所以目前只能参照官网实现局部部署了。
局部安装@commitlint/cli与@commitlint/config-conventional。

1
2
bash
复制代码npm i -D @commitlint/cli @commitlint/config-conventional

在根目录中创建.commitlintrc.js文件,加入以下内容。

1
2
3
4
5
6
7
js
复制代码module.exports = {
extends: [
"@commitlint/config-conventional"
],
rules: {}
};

当然也能像commitizen那样自定义校验规范。可能Commitlint校验规范的某些情况不适合,可通过commitlint-config-cz制定一份符合自己团队的校验规范。
局部安装commitlint-config-cz。

1
2
bash
复制代码npm i -D commitlint-config-cz

在.commitlintrc.js中重新指定extends与rules

1
2
3
4
5
6
7
js
复制代码module.exports = {
extends: [
"cz"
],
rules: {}
};
husky

当您提交或推送时,您可以使用 husky 来检查您的提交消息运行测试、检查代码等Husky 支持所有 Git 钩子
How it works
以一种非常 Linux 的方式,要配置 Git 挂钩,您只需将可执行文本文件放入.git/hooks/, 为了能够运行用户在 .huskyrc.js中创建的任何 Git 钩子,husky 正在将所有可能的钩子安装在.git/hooks/.
例如,当提交时,每个 Git 钩子都会检查是否有相应的钩子定义.huskyrc.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git commit

pre-commit (native) → husky/runner.js (node)
→ is a pre-commit defined in `.huskyrc.js`? → YES, run it

prepare-commit-msg (native) → husky/runner.js (node)
→ is a prepare-commit-msg defined in `.huskyrc.js`? → NO, do nothing

commit-msg (native) → husky/runner.js (node)
→ is a commit-msg defined in `.huskyrc.js`? → NO, do nothing

post-commit (native) → husky/runner.js (node)
→ is a post-commit defined in `.huskyrc.js`? → NO, do nothing

它的好处:用户可以添加、更新和删除钩子,.huskyrc.js并且会自动选择更改。
不利的一面是,即使没有任何东西可以运行,节点也会启动。

【代码检测】lint-staged | husky 提交代码前的检查和修复指定文件

lint-staged 是一个用于在提交代码前检查和修复指定文件的工具。它可以帮助你在提交代码时只对修改的文件进行代码规范检查,而不是全量检查整个项目。(新版本的有点问题,建议使用@13.2.3)
基本使用:(可结合husky)
方式1:
安装代码校验依赖

1
2
3
4
bash
复制代码npm i lint-staged husky -D
npm set-script prepare "husky install" # 在package.json中添加脚本
npm run prepare # 初始化husky,将 git hooks 钩子交由,husky执行
  • 初始化 husky, 会在根目录创建 .husky 文件夹
1
2
bash
复制代码npx husky add .husky/pre-commit "npx lint-staged"
  • pre-commit 执行 npx lint-staged 指令
  • 根目录创建 .lintstagedrc.json 文件控制检查和操作方式
1
2
3
4
5
json
复制代码{
"*.{js,jsx,ts,tsx}": ["prettier --write .", "eslint --fix"],
"*.md": ["prettier --write"]
}

tips

  • 对于 .js、.jsx、.ts 和 .tsx 文件:
    • prettier –write .:将使用 Prettier 对这些文件进行格式化,并将更改写回到文件中。
    • eslint –fix:将使用 ESLint 对这些文件执行代码规范检查,并尝试自动修复一些问题。
  • 对于 .md 文件:
    • prettier –write:将使用 Prettier 对 Markdown 文件进行格式化,并将更改写回到文件中。

具体而言,当你提交代码时,lint-staged 将会遍历暂存区中的文件,并根据配置来执行相应的命令。对于匹配配置中指定的文件类型的文件,lint-staged 将按顺序运行预定义的操作。
例如,如果你对一个 index.js 文件进行了修改并将其添加到 Git 暂存区,当你提交代码时,lint-staged 将按照顺序执行以下操作:

  1. 使用 Prettier 对 index.js 进行格式化,并将更改写回到文件中。
  2. 使用 ESLint 对 index.js 执行代码规范检查,并尝试自动修复一些问题。

同样地,对于一个 README.md 文件的修改,提交代码时,lint-staged 将使用 Prettier 对其进行格式化,并将更改写回到文件中。
注意:如果你想在项目中使用 prettier –write 命令,你需要确保项目中安装了 Prettier 的依赖。
方式2:
自动配置 husky

1
2
3
4
5
6
yarn add husky --dev   # must install

npx husky-init install # npm
npx husky-init # Yarn 1
yarn dlx husky-init # Yarn 2+
pnpm dlx husky-init # pnpm

它将设置 husky,修改package.json并创建一个pre-commit您可以编辑的示例挂钩。默认情况下,它将npm test在您提交时运行。 例如:

把示例 npm test 修改成 yarn lint-staged 或者 您自己定义的命令
在 package.json中添加 lint-staged命令

1
2
3
4
5
6
7
8
9
"lint-staged": "lint-staged --allow-empty",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",

"lint-staged": {
"**/*.{js,jsx,ts,tsx}": "yarn run lint-staged:js",
"**/*.{js,jsx,tsx,ts,less,md,json}": [
"prettier --write"
]
},

例如

在 git commit 的时候,就会触发 .husky/pre-commit 文件下 的命令行 yarn lint-staged或者 您自己定义的命令
在检查代码成功的时候会自动格式化代码然后帮您提交,如果检测到错误就会停止提交并告知错误行,及时改正后可以再次提交
例如

当我git commit时,它会自动检测到不符合规范的代码,如果无法自主修复 则会抛出错误文件给您!
在此之前,配置好 eslint 和 prettier 是有必要的

执行结果:
commit前会有告警报错:
image.png

【commit提交检测】commitlint | @commitlint/config-conventional
  • 安装,并初始化文件:
1
2
npm i commitlint @commitlint/config-conventional -D
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

(注意node版本的匹配)
tipsnpx 是随 Node.js 一起发布的一个用来执行 Node 包里的可执行文件的工具。它的作用类似于直接运行全局安装的 npm 包中的可执行文件,但是它可以临时安装并执行这些包,而不需要将它们作为全局依赖进行安装。
使用 npx 可以方便地运行项目依赖中的命令,而无需手动安装这些依赖。例如,你可以通过以下方式使用 npx 来执行在项目依赖中的可执行文件:

1
bashCopy Codenpx some-package

这将会临时安装 some-package,然后执行它的默认可执行文件。
另外,npx 还可以用来执行本地安装的 npm 包中的命令,例如:

1
bashCopy Codenpx eslint --version

上面的命令将会执行当前目录下安装的 eslint 包,并显示它的版本号。
总之,npx 提供了一种便捷的方式来运行项目依赖中的命令,而无需显式地安装这些依赖。

  • @commitlint/config-conventional 这是一个规范配置,标识采用什么规范来执行消息校验, 这个默认是_Angular_的提交规范
类型 描述
build 编译相关的修改,例如发布版本、对项目构建或者依赖的改动
chore 其他修改, 比如改变构建流程、或者增加依赖库、工具等
ci 持续集成修改
docs 文档修改
feat 新特性、新功能
fix 修改bug
perf 优化相关,比如提升性能、体验
refactor 代码重构
revert 回滚到上一个版本
style 代码格式修改, 注意不是 css 修改
test 测试用例修改
  • commit-msg 钩子执行 消息校验
  • 这里也可以采用自己写的方法,来校验内容, 看下vue@next 的消息提交
  • 我们也可以使用自己写的方法,设置的话使用一下指令

1
npx husky add .husky/commit-msg 'node [dir]/filename.js' # 指定目录文件

需要配置commitlint.config.js:
image.png
新增commitlint.config.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
module.exports = {
// 采用cz自定义的提交规范, > .cz-config.js
extends: ['cz'],
rules: {
'body-leading-blank': [1, 'always'],
'body-max-line-length': [2, 'always', 100],
'footer-leading-blank': [1, 'always'],
'footer-max-line-length': [2, 'always', 100],
'header-max-length': [2, 'always', 100],
'subject-case': [
2,
'never',
['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'type-enum': [
2,
'always',
[
'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test',
],
],
},
prompt: {
questions: {
type: {
description: "Select the type of change that you're committing",
enum: {
feat: {
description: 'A new feature',
title: 'Features',
emoji: '✨',
},
fix: {
description: 'A bug fix',
title: 'Bug Fixes',
emoji: '🐛',
},
docs: {
description: 'Documentation only changes',
title: 'Documentation',
emoji: '📚',
},
style: {
description:
'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)',
title: 'Styles',
emoji: '💎',
},
refactor: {
description:
'A code change that neither fixes a bug nor adds a feature',
title: 'Code Refactoring',
emoji: '📦',
},
perf: {
description: 'A code change that improves performance',
title: 'Performance Improvements',
emoji: '🚀',
},
test: {
description: 'Adding missing tests or correcting existing tests',
title: 'Tests',
emoji: '🚨',
},
build: {
description:
'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)',
title: 'Builds',
emoji: '🛠',
},
ci: {
description:
'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)',
title: 'Continuous Integrations',
emoji: '⚙️',
},
chore: {
description: "Other changes that don't modify src or test files",
title: 'Chores',
emoji: '♻️',
},
revert: {
description: 'Reverts a previous commit',
title: 'Reverts',
emoji: '🗑',
},
},
},
scope: {
description:
'What is the scope of this change (e.g. component or file name)',
},
subject: {
description:
'Write a short, imperative tense description of the change',
},
body: {
description: 'Provide a longer description of the change',
},
isBreaking: {
description: 'Are there any breaking changes?',
},
breakingBody: {
description:
'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself',
},
breaking: {
description: 'Describe the breaking changes',
},
isIssueAffected: {
description: 'Does this change affect any open issues?',
},
issuesBody: {
description:
'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself',
},
issues: {
description: 'Add issue references (e.g. "fix #123", "re #123".)',
},
},
},
}

这样在git提交commit的时候就可以做rules的校验了。
此外,这里的prompt的可以为命令行中做提交提示:需要安装相关依赖【但发现用不了了提示,使用下面的commitizen cz-conventional-changelog】
IDE插件
如果使用webstorm,它有个插件叫git-commit-template可以界面化来引导你按规范提交代码。
如果开发工具是VSCode,就只能用commitizen这个工具在命令行进行提交了。

安装辅助提交依赖:(用于命令行提交的时候的辅助提示)

1
npm i commitizen cz-conventional-changelog -D
  • 安装指令和命令行的展示信息
1
npm set-script commit "git-cz" # package.json 中添加 commit 指令, 执行 `git-cz` 指令

(不行就手动添加)
image.png

  • 初始化编写commit指令
1
npx commitizen init cz-conventional-changelog --save-dev --save-exact

or:

1
yarn commitizen init cz-conventional-changelog --yarn --dev --exact
  • 初始化命令行的选项信息,不然没有选项
  • 此时已经可以根据提示和rules进行提交commit了:
  • image.png

自定义提交规范(可选)

1
npm i -D commitlint-config-cz  cz-customizable
  • 变更 commitlint.config.js ,这里采用自己定义的规范,将会覆盖上面那个,所以上面那个可以不用安装
  • 增加 .cz-config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
'use strict'
module.exports = {
types: [
{ value: '✨新增', name: '新增: 新的内容' },
{ value: '🐛修复', name: '修复: 修复一个Bug' },
{ value: '📝文档', name: '文档: 变更的只有文档' },
{ value: '💄格式', name: '格式: 空格, 分号等格式修复' },
{ value: '♻️重构', name: '重构: 代码重构,注意和特性、修复区分开' },
{ value: '⚡️性能', name: '性能: 提升性能' },
{ value: '✅测试', name: '测试: 添加一个测试' },
{ value: '🔧工具', name: '工具: 开发工具变动(构建、脚手架工具等)' },
{ value: '⏪回滚', name: '回滚: 代码回退' }
],
scopes: [
{ name: 'leetcode' },
{ name: 'javascript' },
{ name: 'typescript' },
{ name: 'Vue' },
{ name: 'node' }
],
// it needs to match the value for field type. Eg.: 'fix'
/* scopeOverrides: {
fix: [
{name: 'merge'},
{name: 'style'},
{name: 'e2eTest'},
{name: 'unitTest'}
]
}, */
// override the messages, defaults are as follows
messages: {
type: '选择一种你的提交类型:',
scope: '选择一个scope (可选):',
// used if allowCustomScopes is true
customScope: 'Denote the SCOPE of this change:',
subject: '短说明:\n',
body: '长说明,使用"|"换行(可选):\n',
breaking: '非兼容性说明 (可选):\n',
footer: '关联关闭的issue,例如:#31, #34(可选):\n',
confirmCommit: '确定提交说明?(yes/no)'
},
allowCustomScopes: true,
allowBreakingChanges: ['特性', '修复'],
// limit subject length
subjectLimit: 100
}
  • package.json 中,将原来commit配置,变更为自定义配置
  • 然后提交会变成这样