基于vite+react+typescript前端开发工程化(二)—— 开发规范
导读
- 基于vite+react+typescript前端开发工程化(一)—— 基础配置
- 基于vite+react+typescript前端开发工程化(二)—— 代码质量
- 基于vite+react+typescript前端开发工程化(三)—— 样式管理
- 基于vite+react+typescript前端开发工程化(四)—— 集成路由
- 基于vite+react+typescript前端开发工程化(五)—— 数据状态管理
- 基于vite+react+typescript前端开发工程化(六)—— 单元测试
- 基于vite+react+typescript前端开发工程化(七)—— 自动化测试
- 基于vite+react+typescript前端开发工程化(八)—— 性能与异常监控
- 基于vite+react+typescript前端开发工程化(九)—— 自动化部署
一、前言
javascript是弱类型语言,前端工程师相对比自由。没有束缚,很容易养成坏习惯。包括笔者在内,偶尔也会偷懒一下。更何况,每个人的做事风格和习惯,各色不一。问题不单单是代码有没有规范,还要考虑代码规范是否统一。制定前端规范,和验证代码质量,是一个合格前端团队必须具备基本素质。
下面我们从代码风格规范、代码质量规范、代码提交规范、版本管理规范几个角度,讨论前端团队如何制定和落地前端规范体系。
二、 编码规范
我们先讨论一下编码规范存在哪些问题?团队成员个人能力参差不齐,个人习惯因人而异。如果没有行之有效的代码规范,会带来各种各样的问题。比如最常见的一些问题包括:
- 因代码风格不同,造成的分支合并冲突
- 因代码不规范,造成代码的可阅读性越来越差
- 糟糕的代码书写习惯,制造的不易察觉的bug
长此以往,随着项目的不断迭代,体量变得越来越大,编码不规范问题,会越来越多,最终造成项目难以维护。
只有制定严格的编码规范,才能最大限度的规避掉诸如此类的问题,同时不断的提高团队成员的编码水平。
从管理角度讲,如何制定优秀的编码规范,既是项目开发的先决要素,也是团队leader必备技能和基本素质。
那么如何制定优秀的前端编码规范呢?
- 要有完整且详细的编码规范文档;
- 要不断向项目组成员强调和明确编码规范的重要性;
- 借助工具和插件,强制性的做一些限制,尽可能的实现自动格式化,解放生产力;
目前前端采用最多的编码规范工具是:
Prettier + Lint + EditorConfig
下面我们,针对上述几个工具,讨论一下如何落地前端规范。
1、editorconfig
EditorConfig有助于在不同的编辑器和IDE
中为同一项目的多个开发人员保持一致的编码风格。EditorConfig由一个用于定义编码风格的配置文件格式和编辑器插件组成,编辑器能够通过插件读取配置文件并遵循定义的规则。EditorConfig文件易于阅读,并且与版本控制系统配合良好。
首先,安装vscode插件 EditorConfig for VS Code
然后,根目录下创建.editorconfig文件,添加如下内容
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
# 表示是最顶层的配置文件,设为 true 时,停止向上查找
root = true
# Unix-style newlines with a newline ending every file
[*]
# 通用配置 -----------
# 缩进方式
indent_style = space
# 设置换行符,值为 lf(换行)、cr(回车) 和 crlf(回车换行)
end_of_line = lf
# 编码格式
charset = utf-8
# 是否删除行尾空格
trim_trailing_whitespace = true
# 缩进大小
indent_size = 4
# 匹配文件配置 -----------
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8
# 4 space indentation
[*.py]
indent_style = space
indent_size = 4
# Tab indentation (no size specified)
[Makefile]
indent_style = tab
# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
很显然,仅仅这些配置还远远不够,编码规范不仅仅要统一编辑器的编码格式,更重要的是要统一开发人员的编码风格。那就需要用到另外一个编码格式利器--Prettier了。
2、Prettier
Prettier是一个专注于代码格式化的工具,对代码不做质量检查。用官方的表述就是:
- 一个“opinionated”的代码格式化工具
- 支持大量编程语言
- 已集成到大多数编辑器中
- 几乎不需要设置参数
Prettier支持很多编程语言,下面这张图可供参考
Prettier支持很多编辑器,下面这张图可供参考
下面我们讨论一下opinionated
,很多框架宣称自己是unopinionated
框架,例如,express,react,vue。也有很多框架是opinionated
框架,例如,Angular。
opinionated
,原则是按照约定规则严格执行;而unopinionated
相对比较自由。opinionated
字面的意思约定的
,因此Prettier是opinionated
会忽略所有原有编代码格式,只保留约定规则。Prettier格式化过程是这样的,首先固有己见
的先把你的代码转换成一种中间状态,叫 AST(Abstract Syntax Tree),然后按照自己的规则,统一格式化代码。
下图中,左侧是源代码,中间是AST,右侧是格式化后的代码。如果有兴趣,可以去prettier playground去尝试体验一下。
更详细的介绍可以参考官网。下面我们一起看一下的Prettier用法
2.1 安装Prettier
yarn add prettier -D
// or
npm install prettier -D
2.2 Prettier命令行用法
- 格式化所有文件
prettier --write .
- 格式化整个目录
prettier --write src/
- 格式化制定文件
prettier --write src/package.json
- 支持glob语法匹配,格式化目录下所有js文件
prettier --write src/**/*.js
2.3 编辑器配置Prettier
大多数情况下,不会单独使用命令行格式化代码。编辑器配合prettier插件,能够更高效的应用Prettier。
- 安装vscode的Prettier插件,插件商店搜索第一个就是
Prettier - Code formatter
- 修改编辑起配置文件settings.json,或者在项目更目录下面创建.vscode/settings.json文件。添加如下内容,这样修改保存文件时,Prettier将自动格式化代码。
{
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
常用编辑器对应的插件
常见的编辑器 | 对应插件名 |
---|---|
VS Code | Prettier - Code formatter |
Emacs | prettier-emacs |
Vim | vim-prettier |
Sublime Text | JsPrettier |
Atom | prettier-atom |
2.4 Prettier配置文件
Prettier使用cosmiconfig来支持配置文件。可以通过以下方式(查找优先顺序)配置Prettier:
- package.json文件中的
prettier
字段 - .prettierrc文件,可以用JSON或YAML编写
- .prettierrc.json, .prettierrc.yml, .prettierrc.yaml, 或者 .prettierrc.json5文件
- 用module.exports导出的配置信息的 .prettierrc.js, .prettierrc.cjs, prettier.config.js, 或者 prettier.config.cjs文件
- .prettierrc.toml 文件
示例
- json格式配置
.prettierrc
文件
{
"useTabs": false,// 是否使用tab进行缩进
"tabWidth": 4, // 缩进空格数
"printWidth": 120, // 最大长度,超过换行
"singleQuote": true, // 是否将双引号转换为单引号
"trailingComma": "none", // 多行使用拖尾逗号
"bracketSpacing": true, // 花括号之间是否添加空格 如 { a:b }
"semi": false // 末尾是否带分号
}
- js格式配置
.prettier.js
文件
module.exports = {
"useTabs": false,
"tabWidth": 4,
"printWidth": 120,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"semi": false
}
- yaml格式配置
.prettierrc.yaml
useTabs: false,
tabWidth: 4,
printWidth: 120,
singleQuote: true,
trailingComma: "none",
bracketSpacing: true,
semi: false
更多配置项可以参考官网相关说明
2.5 Prettier忽略文件
并不是项目中所有文件都需要被格式化。可以配置.prettierignore文件排除掉不想被prettier格式化掉文件
node_modules/
dist/
2.6 结合pre-commit使用
pre-commit是代码从本地提交到共享库,避免污染共享代码库的关键环节。pre-commit是git hooks的一部分,git hooks允许我们在特定的重要动作发生时触发自定义脚本。git hooks脚本文件位于.git/hooks目录,文件默认以.sample结尾。
applypatch-msg.sample # 用来规定提交说明
pre-applypatch.sample # 在补丁应用后但尚未提交前运行
pre-receive.sample # 当从本地版本库完成一个推送之后,远端服务器开始批量更新之前
commit-msg.sample # 用来规定提交说明
pre-commit.sample # commit之前
prepare-commit-msg.sample # 提交信息准备完成后但编辑器尚未启动之前
fsmonitor-watchman.sample
pre-push.sample # push 之前执行
update.sample
post-update.sample # 完成工作区更新之后执行
pre-rebase.sample # 运行在rebase执行之前
如果想启用某个hooks脚本,移除.sample后缀即可。默认脚本是shell,同时也支持perl、rube和python。
比如,启用pre-commit hook
# 创建hook文件
touch pre-commit
输入以下内容
#!/bin/bash
npx prettier --write src/**/*.{ts,tsx,js,scss}
echo 'prettier file success!'
修改文件权限
chmod +ux pre-commit
修改一个.tsx
文件, 然后git commit,你会发现pre-commit脚本命令被执行了。
如果你想绕过pre-commit hook,可以使用下面代码
git commit --no-verify
虽然我们实现了,pre-commit 自动格式化文件功能,但这里依然存在两个问题。
- .git/hooks里的文件并没有共享到代码库,需要手动拷贝
- prettier格式化了所有ts、.tsx、js等文件,这不是一个好的选择,会带来很多问题。比如,如果文件量巨大,会很耗时。
先说第一个问题,pre-commit实现有很多种方式。比如借助npm scripts的pre hook可以实现同样的功能,而且解决了文件共享的问题
// package.json
{
"scripts":{
"commit":"git commit",
"precommit":"npx prettier --write src/**/*.{ts,tsx,js,scss}"
}
}
这里有一个小小的问题,需要用npm run commit
代替git commit
,会造成书写提交信息不方便。其实,后面会讲到提交规范,这个问题其实并不是问题。而且有更好的git hooks管理工具,那就是husky。husky不但可以解决掉这个问题,而且同样可以解决掉hooks脚本共享的问题。下面我们讲解一下husky。
3、Husky
husky是一款优秀的git hooks管理工具,它能帮助我们更方便的管理git hooks脚本。上面我们讨论了git hooks启用pre-commit hook,用以支持自动执行prettier格式化代码。我们讨论了两种方式,但都存在一些问题。husky可以帮助我们解决掉这两个问题。下面我们开始使用husky继续完善我们的项目。
3.1 安装配置Husky
- 用npm 或者 yarn安装husky
npm install husky --save-dev
// 或者
yarn add husky -D
- husky初始化配置,执行以下命令,会在当前目录创建.husky目录,这里将是放置husky hooks的地方
npx husky install
如果想自定义husky hooks目录,可以执行以下命令
husky install .config/husky
- 配置husky自动安装,方便项目组其他成员使用
npm set-script prepare "husky install"
设置结果
// package.json
{
"scripts": {
"prepare": "husky install"
}
}
这样,执行完npm install,将自动执行husky install
初始化husky配置
有关scripts prepare说明:
prepare 是 NPM 操作生命周期中的一环,在执行 install 的时候会按生命周期顺序执行相应钩子:NPM7:preinstall -> install -> postinstall -> prepublish -> preprepare -> prepare -> postprepare
- 添加pre-commit hook
执行下面命令
npx husky add .husky/pre-commit "npx prettier --write src/**/*.{ts,tsx,js,scss}"
将在.husky目录下面创建pre-commit文件,内容如下
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx prettier --write src/**/*.{ts,tsx,js,scss}
这样pre-commit自动执行prettier格式化功能就基本实现了。
3.2 跳过husky hooks
可以使用Git -n/--no-verify选项绕过husky hooks
git commit -m "yolo!" --no-verify
对于不支持--no verify选项的Git命令,可以使用HUSKY环境变量跳过husky
HUSKY=0 git push # yolo!
3.3 卸载husky
npm uninstall husky && git config --unset core.hooksPath
3.4 husky工作原理
下面讲解一下husky的工作原理,了解一下husky是如何解决掉上面两个问题的,以及为什么那样配置。
在v4版本之前husky的工作方式:
因为.git/hooks目录不会共享到远程git库。所以husky需要在两个地方进行配置才能完成一个完整的git hook功能。一个是在package.json或者.huskyrc中配置git hook所要执行的真正命令,一个是在.git/hooks/中配置相对应的git hook。这样才能保证hooks共享和启用。
// package.json
{
"husky": {
"hooks": {
// 提交commit时触发
"pre-commit": "lint-staged",
// 检测commit的message时触发
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
v5版本之后的工作方式:
2016年,Git 2.9引进了core.hooksPath,可以设置Git hooks脚本的目录。 husky可以自定义git hooks目录,这样就可以在一个地方启用和共享git hooks脚本了。需要注意的是,v6版本之后去除了package.json和.huskyrc中配置hooks的功能。仅支持下面这种配置hooks的方式。
husky install .config/husky
husky add .config/husky/pre-commit "npm test"
了解了原理,其实可以采用git命令,设置git hooks目录,手动输入执行命令的方式实现husky相同的功能。
可以指定一个能够共享到git库的目录,作为git hooks目录。然后在该目录创建hook文件,启用git hooks。操作步骤,如下所示:
# 自定义git hooks目录
git config core.hooksPath .config/githooks
# 查看设置 get .config/githooks
git config --get core.hooksPath
# 进入hooks目录
cd .config/githooks
# 创建hook文件
touch pre-commit
# 输入内容
echo #!/bin/sh >> pre-commit
echo prettier --write src/**/*.{ts,tsx,js,scss} >> pre-commit
# 按esc,输入!wq,enter保存
# 权限设置
chmod +ux pre-commit
实现git hooks自定义,步骤相对还是比较繁琐的,采用husky管理更加方便。举例只是为了把原理讲解清楚。有兴趣可以自己尝试体验一下。
4、lint-staged
前面介绍prettier时,遗留下两个问题。其中就有hooks共享的问题,这里我们通过husky完美解决了。
但是还有一个问题有待解决,那就是:
prettier格式化了所有.ts、.tsx、.js等文件,如果大型项目可能会造成性能问题。即使小项目,也不建议对所有文件进行全局格式化。这样做会给测试资源带来庞大的压力,如果不测试prettier,又会带来一定的生产风险。最好的办法是,只对修改文件进行prettier格式化。
如果每次提交只检查本次提交所修改的文件,上面的问题就都解决了。Feedly 的工程师Andrey Okonetchnikov 开发的 lint-staged 就是基于这个思路,lint-staged可以只针对待提交区(staged)的文件做一些处理,例如,只对待提交区的文件做prettier格式化、语法检查、测试等等。
4.1 安装lint-staged
可以使用npm 或者 yarn安装lint-staged
npm install lint-staged -D
# 或者
yarn add lint-staged -D
4.2 lint-staged用法
--help参数可以查看一下命令行基本用法
❯ npx lint-staged --help
用法: lint-staged [options]
Options:
-V, --version # 输出版本号
--allow-empty # 当任务撤消所有分阶段的更改时允许空提交(默认值:false)
-c, --config [path] # 配置文件的路径
-d, --debug # 打印其他调试信息(默认值:false)
-p, --concurrent <parallel tasks> # 要同时运行的任务数,或者为false则要连续运行任务(默认值:true)
-q, --quiet # 自己的控制台输出(默认值:false)
-r, --relative # 将相对文件路径传递给任务(默认值:false)
-x, --shell # 跳过任务解析以更好地支持shell(默认值:false)
-h, --help # 输出用法信息
4.3 配置lint-satged
可以通过以下方式(查找优先顺序)配置lint-staged:
-
package.json文件中的
lint-staged
字段 -
JSON或YAML编写
- .lintstagedrc.json
- .lintstagedrc.yml
- .lintstagedrc.yaml
-
遵循commonjs导出规范
module.exports
的cjs文件:- .lintstagedrc.cjs
- lint-staged.config.cjs
-
遵循ESM导出规则的
export default
的mjs文件:- .lintstagedrc.mjs
- .lint-staged.config.mjs
-
遵循ESM或者commonjs导出规则的js文件,取决于package.json的type字段是不是
module
- lint-staged.config.js
- .lintstagedrc.js
以package.json中配置lint-staged
字段为例:
// package.json
{
"lint-staged": {
"src/**/*.{css,scss}": [
"prettier --write --parser css",
"git add --a"
],
"src/**/*.{ts,tsx,js}": [
"prettier --write",
"git add --a"
]
}
}
然后借助husky,把前面的pre-commit调整一下:
删除掉:
prettier --write src/**/*.{ts,tsx,js,scss}
用husky添加新的hook
npx husky add .husky/pre-commit 'npx --no-install lint-staged'
这样执行git commit命令提交代码时,会自动执行husky下的pre-commit脚本,也即执行lint-staged命令。对暂存区文件执行prettier格式化,而不是针对全部文件。
至此,有关代码风格的规范的配置已经基本完成。但是有关代码质量的规范,并不是prettier和editorconfig目标领域。比如 ===
和==
的使用问题,未使用声明变量的问题,函数是否需要返回值的问题等等,这是lint工具的目标领域,下面讨论一下lint工具是如何保障代码质量,制定代码质量规范的。
三. 代码质量规范
前面我们讲过,代码质量规范属于Linter工具功能范畴。在应用Linter工具制定代码质量规范之前,先简单了解一下Linter工具。
1、Linter工具功能概述
Eslint、Stylelint、Tslint(已废弃)
都属于Linter工具,它们通过对代码的AST进行分析,并按照一系列可配置的规则,为用户提供代码校验的功能。他们的规则主要分为两大类:Formatting Rules和Code-quality Rules。
Eslint是目前最流行的前端代码检查工具,Tslint已经废弃,投奔Eslint阵营。
在Eslint之前应用最广泛的代码质量检查工具有Jslint和Jshint。
Jslint是由Douglas Crockford(《JavaScript 语言精粹》作者)开发的。虽然给前端代码质量检查带来了很大的帮助,但所有规则都是内置并且不可扩展,这给开发者带来很多不便,于是Anton Kovalyov基于Jslint开发了Jshint。Jshint在Jslint的基础上提供了丰富的配置项,给了开发者极大的自由。
Eslint是由 Nicholas C. Zakas (《JavaScript 高级程序设计》作者) 于2013年6月创建。起初由于性能问题,相对JSHint并没有优势。但随着ES6的普及。Eslint的高扩展性,得到了Babel阵营的支持,逐渐的成为主流。后来又整合了JSCS,逐渐的成为最流行的Linter工具。
Linter是一种是一种静态的代码检查分析工具,常用于寻找有问题的模式或者代码,并且不依赖于具体的编码风格。它有很多优势:
- 避免低级bug,找出可能发生的语法错误
- 提示删除多余的代码
- 确保代码遵循最佳实践
- 统一团队的代码风格
下面我们应用ESLint和StyleLint讨论一下前端代码质量规范。
2、Eslint
2.1 安装配置eslint
npm install eslint -D
# 或者
yarn add eslint -D
当然也可以全局安装
npm install eslint -g
- 初始化配置eslint
输入命令,开始生成eslint初始化配置:
npm init @eslint/config
依次选择对应的配置
✔ How would you like to use ESLint? · To check syntax, ... enforce code style
✔ What type of modules does your project use? · JavaScript modules (import/export)
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · Use a popular style guide
✔ Which style guide do you want to follow? · airbnb
✔ What format do you want your config file to be in? · JavaScript
自动安装依赖
+ eslint-plugin-jsx-a11y@6.5.1
+ eslint-plugin-react@7.28.0
+ eslint-plugin-import@2.25.4
+ eslint-config-airbnb@19.0.4
+ eslint@8.8.0
+ eslint-plugin-react-hooks@4.3.0
+ @typescript-eslint/parser@5.10.2
+ @typescript-eslint/eslint-plugin@5.10.2
自动生成.eslintrc.js配置文件
我们以airbnb代码规范为基础,当然react官方推荐的代码规范是必不可少的。当然你还可以选择eslint:recommended(eslint官方) 、eslint-config-ali(阿里前端)、eslint-config-alloy(AlloyTeam)等代码规范
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:react/recommended',
'airbnb',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: [
'react',
'@typescript-eslint',
],
rules: {
},
};
2.2 eslint 命令行
可以控制台输入以下命令,检查项目中的ts和tsx文件
npx eslint src/**/*.{ts,tsx}
不出意外,将会打印很多不符合eslint代码规则的异常信息
...src/App.test.tsx
1:19 error Strings must use singlequote quotes
2:24 error Strings must use singlequote quotes
3:26 error Strings must use singlequote quotes
4:23 error Unable to resolve path to module './app/store' import/no-unresolved
4:23 error Missing file extension for "./app/store" import/extensions
4:23 error Strings must use singlequote quotes
5:17 error Unable to resolve path to module './App' import/no-unresolved
5:17 error Missing file extension for "./App" import/extensions
5:17 error Strings must use singlequote quotes
7:1 error 'test' is not defined no-undef
7:6 error Strings must use singlequote quotes
9:5 error JSX not allowed in files with extension '.tsx' react/jsx-filename-extension
11:16 error Missing trailing comma comma-dangle
14:3 error 'expect' is not defined no-undef
.......
✖ 102 problems (96 errors, 6 warnings)
38 errors and 0 warnings potentially fixable with the `--fix` option.
异常信息说明,项目中的ts和tsx文件中共有102
代码质量问题需要处理,其中错误有96
个,警告有6
个。而且有38
个错误,可以用--fix
参数自动修复。
执行命令,自动修复
npx eslint src/**/*.{ts,tsx} --fix
部分错误是被自动修复了
65 problems (59 errors, 6 warnings)
我们把上述命令配置到package.json scripts,在需要用到lint可以直接使用npm run lint
。
另外,我们还可以配置prebuild
hook,每次npm run build
之前都会自动执行eslint检验。
{
"scripts":{
"lint": "eslint src/**/*.{ts,tsx} --fix",
"prebuild": "npm run eslint || exit 1",
"build": "vite build"
}
}
上面我们采用glob匹配的方式查找目标文件,还可以用--ext
指定扩展名
比如eslint验证lib目录下的.ts和.tsx文件
eslint --ext .tsx --ext .ts lib/
# 或者
eslint --ext .tsx,.ts lib/
-c
, --config
参数指定配置文件
eslint -c ~/my-eslint.json file.ts
Eslint同样支持很多类型配置文件,如果同一个目录下有多个配置文件,Eslint 只会使用一个。优先级顺序如下:
.eslintrc.js,
.eslintrc.yaml,
.eslintrc.yml,
.eslintrc.json,
.eslintrc,
package.json # eslintConfig字段
更多命令行信息可以参考这里
更多配置文件相关信息参考这里
2.3 配置编辑器
如同prettier,大多数情况eslint同样结合编辑器和插件一起使用。
- vs code安装 eslint插件
- 配置vs code,.vscode目录中settings.json文件中添加如下配置
{
"editor.codeActionsOnSave": {
// 启用保存文件自动修复
"source.fixAll.eslint": true
},
"eslint.codeAction.showDocumentation": {
// 启用文档提示
"enable": true
},
"eslint.options": {
// 指定vscode的eslint所处理的文件的后缀
"extensions": [".js", ".ts", ".tsx"]
},
"eslint.validate": [
"javascript",
"javascriptreact",
"html",
"react",
"typescript",
"typescriptreact"
]
}
这样修改保存文件,将自动执行eslint自动验证和修复当前文件。
2.4 eslint配合husky和lint-staged使用
同样为了提高性能,只对修改过的文件,做增量检查。我们同样把eslint结合husky和lint-staged一起使用。前面我们已经配置过prettier结合husky和lint-staged。这里只需要稍加改动,把eslint --fix命令添加进去即可。
调整后的lint-staged字段如下:
// package.json
{
"lint-staged": {
"src/**/*.{scss,css}": [
"prettier --write --parser css",
"git add --a"
],
"src/**/*.{ts,tsx,js}": [
"eslint --fix",
"prettier --write", // 增加
"git add --a"
]
}
}
2.5 解决Eslint和Prettier格式化冲突
Eslint的格式化功能,跟prettier有重叠,可能会产生冲突。比如tabWidth和indent,max-len和printWidth。而且Prettier和Linter在代码格式化的思路有本质上的不同:
- Linters的格式化思路是:给我一个规则,如果不符合这个规则,我才会去格式化
- Prettier的格式化思路是:给我一个规则,如果不符合这个规则,我按照A格式格式化,如果符合这个规则,我按照B格式格式化
比如,在不超出max-len的条件下,如下两种写法eslint是允许的,而prettier会格式化代码,但跟超出printWidth的情况格式化规则又不同。很显然prettier做统一代码风格的工具更靠谱。
// 小明写的
const {a, b, c} = obj
// 小李写的
const {
a,
b,
c,
} = obj
采用prettier的格式化风格,需要用到eslint-plugin-prettier
插件。而且还需要禁用掉eslint中跟prettier冲突的规则,这个eslint-config-prettier
工具已经帮我们做好了,直接使用即可。
安装上面两个工具
npm install eslint-plugin-prettier eslint-config-prettier -D
在.eslintrc.js增加如下配置
{
extends:[
'airbnb',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'prettier'
],
plugins:[
'react',
'@typescript-eslint',
'prettier'
]
}
其中extends按照规则eslint-config-*
查找依赖,plugins按照规则eslint-plugin-*
查找依赖。项目整体将采用 prettier格式化风格。
eslint-confit-prettier@^8.0.0以后,已经把几个相关模块整合到了一起,因此extends里面只需要写
prettier
2.6 解决eslint验证相关问题
前面执行eslint验证,报出了很多不符合规范的异常信息。虽然我们自动--fix
掉一些错误的代码格式,但是还是有很多信息需要手动处理。
- import相关问题
Unable to resolve path to module "**"
Unable to resolve path to module "**"
解决这两个问题需要借助eslint-plugin-import
插件,如果你还有印象,前面eslint --init
的时候,已经安装过这个插件,可以直接使用。
.eslintrc.js文件中增加两个配置,即可解决掉这个问题
{
extends:[
'airbnb',
'plugin:react/recommended',
'plugin:import/typescript', // 增加
'plugin:@typescript-eslint/recommended',
'prettier'
],
plugins:[
'react',
'@typescript-eslint',
'prettier'
],
rules:{
// 增加
'import/extensions': [
'error',
'ignorePackages',
{
ignorePackages: true,
pattern: {
js: 'never',
mjs: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never'
}
}
],
}
}
- export相关问题
默认规则要求模块必须有export default
导出语句
可以rules字段中添加以下配置项禁用:
{
rules:{
'import/prefer-default-export': 0,
}
}
当然还可以按照自己的需要配置更多的选项:
{
rules:{
'no-param-reassign': 0, // 允许参数重新赋值
'no-debugger': 2, // 禁止出现debugger
'no-alert': 2, // 禁止使用alert confirm prompt
'class-methods-use-this': 0, // 关闭类方法中必须使用this
'import/prefer-default-export': 0, // 关闭模块须有export default
'no-dupe-keys': 2, // 禁止对象中出现同名key
'no-dupe-args': 2, // 禁止出现同名参数
'no-use-before-define': [2, { functions: false }] // 除函数外,必须在应用前声明
}
}
再次执行命令验证项目
npx eslint --fix src/**/*.{ts,tsx}
只有6个警告信息了
✖ 6 problems (0 errors, 6 warnings)
建议多去尝试配置一些符合自己团队习惯的规则,逐渐的形成一套自己的规范体系。但是有一点需要说明,不要滥用off,那是一种掩耳盗铃的行为,不报错不等没有错误。始终要牢记自己的初衷,引入linters工具的目的是为了制定良好的编码规范,让大家养成良好的编码习惯。
3、Stylelint
Eslint只能验证js和ts的代码质量,css的代码质量需要借助StyleLint验证。
3.1 安装Stylelint
如果仅仅是验证css,安装下面这些依赖即可
npm install --save-dev stylelint stylelint-config-standard stylelint-config-prettier
stylelint-config-standard
是官方推荐的配置规则,stylelint-config-prettier
是为了关闭跟prettier冲突的配置规则,因为prettier做格式化代码的工具更合理。
3.2 配置Stylelint
stylelint使用cosmiconfig
查找配置信息。查找顺序:
package.json
文件中的stylelint
属性.stylelintrc
文件,支持JS、JSON 和 YAML,后缀(.js, .json, .yaml, .yml)stylelint.config.js
export 配置对象- 如果
package.json
中配置了"type":"module",查找stylelint.config.cjs
创建配置文件.stylelintrc.js
,输入以下内容
module.exports = {
root: true,
extends: ['stylelint-config-standard'],
rules: {
indentation: 2,
'function-url-quotes': 'never'
}
}
3.3 添加支持sass配置
如果项目中使用了预处理器sass,官方推荐使用stylelint-config-standard-scss
这个配置规则。
这个配置规则extend了stylelint-config-standard
和 stylelint-config-recommended-scss
,适合大多数应用场景。
相应的关闭跟prettier冲突的配置规则,推荐使用stylelint-config-prettier-scss
这个配置规则,这个配置规则extend了stylelint-config-prettier
,并且关闭了一些跟prettier冲突的配置项。
首先安装
npm install --save-dev stylelint-config-standard-scss stylelint-config-prettier-scss
调整配置文件中的extends,stylelint-config-prettier-scss
应该放在后面,才能覆盖前面的配置。而且既能验证css文件也能验证sass文件。
extends: ['stylelint-config-standard-scss','stylelint-config-prettier-scss'],
3.4 使用stylelint验证代码
- 命令行
输入以下命令验证项目中的样式文件
npx stylelint --fix src/**/*.{css,scss}
- 配合编辑器一起使用
安装vscode插件
编辑.vscode/settings.json
,添加以下配置
{
// ...
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"stylelint.validate": ["css", "postcss", "scss", "sass"],
"editor.codeActionsOnSave": {
// ...
"source.fixAll.stylelint": true
},
// ...
}
编辑sass文件并保存,将会自动执行stylelint --fix验证和修复当前文件。
3.5 结合husky和lint-staged使用
调整lint-staged字段,添加stylelint命令。
"lint-staged": {
"scr/**/*.{css,scss}": [
"stylelint --fix",
"prettier --write --parser css"
],
"src/**/*.{ts,tsx,js}": [
"eslint --fix",
"prettier --write"
]
}
3.6 添加自定义配置项
可以给stylelint添加符合团队习惯的配置规则。把stylelint规则添加到.stylelintrc.js文件的rules配置项即可。
例如:
{
rules:{
'color-hex-case': 'lower', // 16进制颜色小写
"string-quotes":"single", // 单引号
"number-leading-zero": "never", // 小数不带0
}
}
更多配置可以查看官网
3.7 stylelint忽略配置
可以创建.stylelintignore文件,配置忽略stylelint验证的文件
*.js
*.ts
*.tsx
*.jpg
*.png
*.ttf
*.svg
node_modlue/**/*
dist/**/*
在 .stylelintrc.js
內设定需要忽略的文件
{
ignoreFiles: ["dist/**/*", "src/assets/scss/abc.scss"]
}
忽略整个文件,在首行加入 /* stylelint-disable */
/* stylelint-disable */
html {}
忽略多行
/* stylelint-disable */
html {}
.div {
color: red;
}
/* stylelint-enable */
忽略一行, 在样式前加入 /* stylelint-disable-next-line */
以忽略该行
#app {
/* stylelint-disable-next-line */
color: pink !important;
}
四、 代码提交规范
前面我们讨论了代码质量的提交规范,通过linters工具,结合hooks管理工具husky,以及lint-staged工具,完成了代码质量检查和自动化的基本协作体系。基本可以满足目前我们大部分的代码质量检查需求。下面我们一起讨论一下,提交信息(Commit Message)的规范问题。通常应用最多的代码共享管理工具是git,我们下面还是基于git展开讨论。
1、为什么要规范Commit Message?
-
良好的代码提交信息,是团队协作的基础
项目开发是一个团队协作的过程,需要理解其他团队成员都做了哪些事情。git log帮我们记录下了所以成员都提交信息,以及对应的文件和修改记录。提交说明是其中最重要的参考依据。如果团队没有严谨的提交规范,提交信息不会被重视,提交说明会趋于简单化,粗略的写几个字,可能时间长了,自己都不知道修改了哪些信息,别人更没办法清晰的了解提交内容。严谨的提交信息规范,能帮助团队书写,跟完善,跟容易阅读的提交说明。
-
方便生成版本发布日志(change log)
严谨、规范的提交说明,借助相关工具更容易生成代码的版本发布日志(change log)。 借助版本发布日志,可以更清晰的了解版本信息。
-
便于查找历史提交信息
比如,下面的命令仅仅显示本次发布新增加的功能
git log <last release> HEAD --grep feature
2、Commit Message格式
目前规范使用较多的是 Angular 团队的规范,继而衍生了 Conventional Commits specification,约定式提交规范
。约定式提交规范
是一种基于提交消息的轻量级约定。 它提供了一组用于创建清晰的提交历史的简单规则;这使得编写基于规范的自动化工具变得更容易。很多工具也是基于此规范,它的 Commit Message 包括三个部分:
Header,Body 和 Footer。
<type>: <subject> # Header
<BLANK LINE> # 空行
<body> # Body
<BLANK LINE> # 空行
<footer> # Footer
其中Header 是必需的,Body 和 Footer 可以省略。Commit Message格式要求,可以参考以下描述:
# 标题行:50个字符以内,描述主要变更内容
#
# 主体内容:更详细的说明文本,建议72个字符以内。 需要描述的信息包括:
#
# * 为什么这个变更是必须的? 它可能是用来修复一个bug,增加一个feature,提升性能、可靠性、稳定性等等
# * 他如何解决这个问题? 具体描述解决问题的步骤
# * 是否存在副作用、风险?
#
# 如果需要的话可以添加一个链接到issue地址或者其它文档
一个完整且规范的提交信息,应当尽量符合上述要求。标题能描述提交信息的类型,主题内容尽量可以回答三个问题:为什么做?怎么做?影响范围如何?如果有非兼容性的改动,应该尽量在Footer部分描述清楚,或者给出描述信息的链接。
2.1 Header说明
Header部分,包括三个字段:type
(必需)、scope
(可选)和subject
(必需)
2.1.1 Type表示提交类别
Type表示提交信息的类别,不能随意填写,主要包含以下几种类别:
- feat:新功能(feature)
- fix:修补bug
- docs:文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor:重构(即不是新增功能,也不是修改bug的代码变动)
- perf: 性能提升(提高性能的代码改动)
- test:测试
- build:构建过程或辅助工具的变动(webpack等)
- ci:更改CI配置文件和脚本
- chore:不修改src或测试文件的其他更改
- revert:撤退之前的commit
2.1.2 scope表示影响范围
scope
用于说明 commit 影响的范围,比如数据层、控制层、视图层、组件、router等,根据不同的项目,范围的划分可能会不同。
简单讲:
Type描述了调整什么类型的信息?
Scope描述了调整哪个模块的信息?
2.1.3 subject是标题描述
subject
是 commit 目的简短描述,不超过50个字符。
2.2 Body说明
Body部分是对本次提交的详细描述,应当尽量把前面提到的三个问题描述清楚。
- 此次变更解决了什么问题?
- 如何解决这些问题?
- 是否存在副作用或者风险?
2.3 Footer说明
Footer部分主要针对两问题:
- 非兼容性的变动(BREAK CHAGNE)
如果当前代码与上一个版本不兼容,有破坏性的变动,则 Footer 部分以BREAKING CHANGE
开头,后面是对变动的描述、以及变动理由和迁移方法。
- 关闭issue
如果当前 commit 针对某些issue,那么可以在 Footer 部分关闭这些 issue
关闭单个 issue
Closes #234
关闭多个 issue
Closes #123, #245, #992
3、commit模版
3.1 git默认模版信息
命令行输入以下信息即可看到默认的git 默认模版信息
git commit
输出:
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
# ....
.git/COMMIT_EDITMSG" 42L, 1233C
上述信息,其实是.git/COMMIT_EDITMSG中的内容。可以看作是git默认的Commit Message模版。
按照模版提示,填写相应的提交信息,保存退出,即可把改动信息从暂存区提交到版本库。
3.2 自定义git Commit Message模版
为了规范提交信息,可以按照自己团队需求,自定义Commit Message模版
首先创建模版文件
cd ~
touch .gitmessage.txt
输入自定义模版信息,保存并退出
# 请按照下面信息说明填写提交信息,建议修改相应<>中的内容,并去掉行首的#和<>
# 类型字段包含:
# feat:新功能(feature)
# fix:修复bug
# doc:文档(documentation)
# style: 格式化 ESLint调整等(不影响代码运行的变动)
# refactor:重构(即不是新增功能,也不是修改bug的代码变动)
# test:增加测试
# 影响范围:
# 用于说明 commit 影响的范围,比如修改的登录页、账户中心页等
# 主题:
# commit目的的简短描述,不超过50个字符
# <类型>:(影响范围) <主题>
# Body 部分是对本次 commit 的详细描述,可以分成多行
# <变更信息的内容以及原因?>
# <解决问题的过程简述>
# <有什么影响?>
# Footer用来关闭 Issue或以BREAKING CHANGE开头,后面是对变动的描述、
# BREAK CHANGE: <break change>
# Close <#123>, <#456>
然后在命令行输入以下命令,即可把.gitmessage.txt设置成git commit message义模版:
git config --global commit.template ~/.gitmessage.txt
或者修改.gitconfig文件,输入以下内容,同样可以达到自定义模版的目标
[commit]
template = ~/.gitmessage.txt
配置完成后,可以自行尝试执行git commit
命令,看一下是否输出上述自定义模版信息
4、commitlint
前面我们讲过,这种提交信息规范其实是一种约定式提交规范
。既然是约定,就没有办法保证所有人都能正确的理解提交规范,也没有办法保证所有人都能按照规范一步步正确的填写提交信息,即使大家都能理解提交规范,也有意愿按照规范提交信息,但没办法保证所有人都不会犯错。所以提交信息还需要检查工具做保障,保证提交信息的规范性。
commitlint 是当前使用最为广泛的 git commit 校验约束工具之一。可以帮助我们,像应用Linters工具校验代码一样,校验提交信息的规范性。
4.1 安装commitlint
npm install --save-dev @commitlint/config-conventional @commitlint/cli
@commitlint/cli
是commitlint的核心文件和命令行工具,@commitlint/config-conventional
是严格遵循conventional commits规范的共享配置文件。结合两个工具,可以实现提交信息的规范验证。
4.2 commitlint配置文件
以下文件都可以作为commitlint的配置文件
commitlint.config.js
.commitlintrc.js
.commitlintrc
.commitlintrc.json
.commitlintrc.yml
package.json
文件中commitlint
字段
输入以下命令创建commitlint配置文件
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > .commitlintrc.js
4.3 配置commit-msg hook
实现commitlint自动对提交的代码进行检验,还需要配置commit-msg hook。命令行输入以下命令,配置husky的commit-msg hook
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
如果你使用yarn,使用下面命令
yarn husky add .husky/commit-msg 'yarn commitlint --edit $1'
以下是一条不规范的提交信息,验证一下commitlint是否起作用
git commit -m "test commitlint"
输出提交失败信息:
⧗ input: test commitlint
✖ subject may not be empty [subject-empty] # subject不能为空
✖ type may not be empty [type-empty] # type不能为空
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
error Command failed with exit code 1.
输入符合规范的提交信息
git commit -m "feat: http"
成功:
[master c1f3beb] feat: http
2 files changed, 1 insertion(+), 5 deletions(-)
5、commitizen
基于commitlint,我们对提交信息(commit message),制定了强制性的提交规范。不符合规范要求的提交信息,将无法从暂存区提交到版本库。但这样做需要付出一定的成本,虽然借助工具达到了强制符合规范的目的,但同时也会增加开发者的负担。尽管收益远大于成本,但是更好的办法是提高生产力,尽可能的减少应用负担。
我们可以参照表单的设计思路,设计一个类似表单的cli工具,只需要按照问题和提示,一步步填写特定信息,就可以生成标准的提交信息。commitizen就是这样的工具,一个书写符合规范的commit message的工具。
有两种可靠的方式使用commitizen:
-
commitlint提供了commitizen适配器
(@commitlint/cz-commitlint)
。commitlint负责提交信息规范校验,commitizen负责生成符合规范提交信息。 -
借助commitizen/cz-cli和commitizen适配器
cz-conventional-changelog
或者cz-customizable
,生成符合规范的提交信息
5.1 @commitlint/cz-commitlint适配器
@commitlint/cz-commitlint是commitlint官方提供的,可以替代@commitlint/prompt的适配器。@commitlint/cz-commitlint可以和commitlint共用配置文件。配置项主要包括:messages和questions两部分。
5.1.1 安装@commitlint/cz-commitlint
npm install @commitlint/cz-commitlint commitizen -D
5.1.2 配置.commitlintrc.js 和 commitizen
package.json中配置commitizen
"config": {
"commitizen": {
"path": "@commitlint/cz-commitlint"
}
}
.commitlintrc.js中添加配置信息
module.exports = {
extends: ['@commitlint/config-conventional'],
prompt: {
settings: {
enableMultipleScopes: true, // 支持多scope
scopeEnumSeparator: ',' // 多scope分隔符
},
messages: {
skip: '<可跳过>',
max: '最多输入 %d 个字符',
min: '至少需要输入 %d 个字符',
emptyWarning: '不能为空',
upperLimitWarning: '超过长度限制',
lowerLimitWarning: '未达到最少数字要求'
},
questions: {
type: {
description: '选择你要提交的信息类型 ',
enum: {
feat: {
description: '新功能',
title: 'Features',
emoji: '✨'
},
fix: {
description: '修复bug',
title: 'Bug Fixes',
emoji: '🐛'
},
docs: {
description: '书写文档',
title: 'Documentation',
emoji: '📚'
},
style: {
description: '代码格式化(空格, 格式化, 分号等)',
title: 'Styles',
emoji: '💎'
},
refactor: {
description: '代码重构',
title: 'Code Refactoring',
emoji: '📦'
},
perf: {
description: '性能优化提升',
title: 'Performance Improvements',
emoji: '🚀'
},
test: {
description: '测试',
title: 'Tests',
emoji: '🚨'
},
build: {
description: '调整构建或者依赖',
title: 'Builds',
emoji: '🛠'
},
ci: {
description: '调整持续集成',
title: 'Continuous Integrations',
emoji: '⚙️'
},
chore: {
description: '变更构建流程或者辅助工具',
title: 'Chores',
emoji: '♻️'
},
revert: {
description: '代码回退',
title: 'Reverts',
emoji: '🗑'
}
}
},
scope: {
description: '提交信息类型(模块、组件、页面)'
},
subject: {
description: '简洁明了的修改摘要'
},
body: {
description: '详细的调整信息描述'
},
isBreaking: {
description: '是否有非兼容性的调整?'
},
breaking: {
description: '请输入非兼容调整的详细描述'
},
isIssueAffected: {
description: '是否有关闭 issue'
},
issues: {
description: '列举关闭的 issue (例如 "fix #123", "re #123")'
}
}
}
}
git commit
命令需要统一调整为 git-cz
git add --a
npx git-cz
输出显示如下信息:
- 选择type
? 选择你要提交的信息类型: (Use arrow keys)
❯ feat: 新功能
fix: 修复bug
docs: 书写文档
style: 代码格式化(空格, 格式化, 分号等)
refactor: 代码重构
perf: 性能优化提升
test: 测试
(Move up and down to reveal more choices)
- 选择scope
? 提交信息类型(如:模块或组件名称) <可跳过>: 至多95个字符
(0)
- 填写subject
? 简洁明了的修改摘要: 至多83个字符
(0)
- 填写body信息
? 详细的调整信息描述 <可跳过>:
- 是否有非兼容性调整
? 是否有非兼容性的调整?: (y/N)
- 是否关闭了 issue
? 是否有关闭 issue: (y/N)
5.2 配置cz-cli和cz-conventional-changelog
安装cz-conventional-changelog
npm install cz-conventional-changelog -D
删除掉.commitlintrc.js中的@commitlint/cz-commitlint适配器的配置
调整package.json中的config
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
执行git-cz命令,输出如下信息,跟@commitlint/cz-commitlint适配器基本完全一致
因为:
The interactive process is inspired by cz-conventional-changelog.
? Select the type of change that you're committing: (Use arrow keys)
❯ feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactor: A code change that neither fixes a bug nor adds a feature
perf: A code change that improves performance
test: Adding missing tests or correcting existing tests
(Move up and down to reveal more choices)
5.3 配置cz-customizable
cz-customizable允许我们像使用@commitlint/cz-commitlint一样,自定义适合自己项目的配置方案。可以自定义type和scope,而且同样支持配置成中文。
安装cz-customizable
npm install cz-customizable --save-dev
调整commitizen配置
// package.json
"config": {
"commitizen": {
"path": "cz-customizable"
}
}
创建.cz-config.js文件,配置自定义信息
touch .cz-config.js
配置信息如下
module.exports = {
types: [
{
value: 'feat',
name: 'feat: 新增功能'
},
{
value: 'fix',
name: 'fix: 修复bug'
},
{
value: 'init',
name: 'init: 初始化'
},
{
value: ':pencil2: docs',
name: 'docs: 文档变更'
},
{
value: 'style',
name: 'style: 代码格式化'
},
{
value: 'refactor',
name: 'refactor: 功能重构'
},
{
value: 'perf',
name: 'perf: 性能优化'
},
{
value: 'test',
name: 'test: 测试编码'
},
{
value: 'revert',
name: 'revert: 代码回退'
},
{
value: 'build',
name: 'build: 变更构建打包工具(webpack、vite、npm、gulp等)'
},
{
value: 'chore',
name: 'chore: 变更构建流程或者辅助工具'
},
{
value: 'ci',
name: 'ci: 更改持续集成'
}
],
messages: {
type: '请选择提交类型(必填)',
scope: '请输入文件修改范围(可选)',
subject: '请简要描述修改内容(必填)',
body: '请详细描述修改内容,多条可用"|"拆分(可选)',
breaking: '请列出非兼容性重大的变更 BREAKING CHANGES(可选)',
footer: '请列出修复并关闭的issue(可选),例如: #25, #100',
confirmCommit: '确定提交信息内容吗?'
},
scopes: [
['build', '打包工具'],
['mock', 'mock服务'],
['config', '基础配置'],
['lint', 'lint验证'],
['components', '组件模块'],
['pages', '调整页面'],
['services', '调整服务'],
['http', 'http请求工具'],
['router', '路由工具'],
['utils', 'utils 相关'],
['themes', '主题调整'],
['styles', '样式调整'],
['deps', '项目依赖'],
['store', 'vuex管理'],
['hooks', 'hooks管理'],
['other', '其他修改'],
// 如果选择 custom ,后面会让你再输入一个自定义的 scope , 也可以不设置此项, 把后面的 allowCustomScopes 设置为 true
['custom', '以上都不是?我要自定义']
].map(([value, description]) => {
return {
value,
name: `${value.padEnd(30)} (${description})`
}
}),
allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix', 'refactor'], // 当提交类型为feat、fix时才有破坏性修改选项
subjectLimit: 72 // 摘要字符长度限制
}
执行git-cz命令,输出如下信息,表示配置成功。这也是本项目采用的配置方式。
? 请选择提交类型(必填) (Use arrow keys)
❯ feat: 新增功能
fix: 修复bug
init: 初始化
docs: 文档变更
style: 代码格式化
refactor: 功能重构
perf: 性能优化
(Move up and down to reveal more choices)
五、 版本管理规范
1、变更日志(change log)
变更日志是对项目所做更改的详细记录,通常包括修复和新功能。变更日志通常由按时间顺序排列的列表组成,详细列出已进行的更改以及更改的执行者。变更日志文件通常被组织成段落,描述与特定目标相关的所有更改。每个段落通常以更改日期、作者姓名和电子邮件地址开头。列出每个修改过的文件的名称,以及被更改的功能或部分。还经常提供关于更改的简要原因和一些详细信息。
变更日志在涉及许多开发人员的项目中至关重要,尤其开源项目。在任何项目中,变更日志都是有用的,因为了解以前的版本与当前版本的不同之处可能很重要。例如,发行说明通常基于项目变更日志,通常包括缺陷修复和产品增强。
2、生成变更日志(change log)
自动生成change log
是建立在约定式提交的基础上。前面我们已经详细讨论和实现了约定式提交规范。已经具备自动生成change log
的条件,只要合适的工具,就能方便快速的自动生成change log
。常用的工具主要有下面几个:
2.1 conventional-changelog-cli
安装
npm install -g conventional-changelog-cli
在package.json,配置生成变更日志的npm script命令:
{
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
}
上面的命令只生成后续新的日志信息,不会覆盖前面到日志信息。而且仅提取匹配“功能(feat)”、“修复(fix)”、“性能改进(perf)”或“破坏性变更(refactor)”等类型的commit信息,生成日志信息到CHANGELOG.md文件。如果想重新生成全部的日志信息,需要用以下命令,这会覆盖前面生成的日志信息。
conventional-changelog -p angular -i CHANGELOG.md -s -r 0
conventional-changelog-cli有相应的推荐工作流程:
- 提交修改信息 commit change
- 修改
package.json
中的version
- 执行
npm run changelog
生成日志 - 提交package.json和Changelog.md文件
- 打tag
- push到远程git库
这里有两点需要说明:
- 生成日志之前需要,修改版本号
- 生成日志之后再打tag,保证新的release版本中包含最新的changelog信息
2.2 npm 版本规则
这里涉及到了,npm version管理,我们简单介绍一下,这属于前端必备知识点,需要牢记并掌握。如果对npm version比较熟悉,可以跳过本小节。
2.2.1 版本的格式
版本格式主要
由下面几部分构成,
major.minor.patch**
均为非负整数,且禁止在数字前方补零
major:主版本号 - 重构升级,破坏性非兼容性修改比较多
minor:次版本号 - 向下兼容的功能性新增,或者部分非兼容性修改
patch:修订版本号 - 通常是bug修复性的版本
**:表示先行版本(预发布版本) 如:0.1.1-1、0.1.1-beta、0.1.0-2
2.2.2 版本匹配规则
version
必须匹配某个版本 如:1.1.2,表示必须依赖1.1.2版
>version
必须大于某个版本 如:>1.1.2,表示必须大于1.1.2版
>=version
可大于或等于某个版本
如:>=1.1.2,表示可以等于1.1.2,也可以大于1.1.2版本
<version
必须小于某个版本
如:<1.1.2,表示必须小于1.1.2版本
<=version
可以小于或等于某个版本
如:<=1.1.2,表示可以等于1.1.2,也可以小于1.1.2版本
~version
大概匹配某个版本
如果minor版本号指定了,那么minor版本号不变,而patch版本号任意
如果minor和patch版本号未指定,那么minor和patch版本号任意
如:~1.1.2,表示>=1.1.2 <1.2.0,可以是1.1.2,1.1.3,1.1.4,.....,1.1.n
如:~1.1,表示>=1.1.0 <1.2.0,可以是同上
如:~1,表示>=1.0.0 <2.0.0,可以是1.0.0,1.0.1,1.0.2,.....,1.0.n,1.1.n,1.2.n,.....,1.n.n
^version
兼容某个版本
版本号中最左边的非0数字的右侧可以任意
如果缺少某个版本号,则这个版本号的位置可以任意
如:^1.1.2 ,表示>=1.1.2 <2.0.0,可以是1.1.2,1.1.3,.....,1.1.n,1.2.n,.....,1.n.n
如:^0.2.3 ,表示>=0.2.3 <0.3.0,可以是0.2.3,0.2.4,.....,0.2.n
如:^0.0,表示 >=0.0.0 <0.1.0,可以是0.0.0,0.0.1,.....,0.0.n
x标识符
x的位置表示任意版本
如:1.2.x,表示可以1.2.0,1.2.1,.....,1.2.n
*标识符
任意版本,""也表示任意版本
如:*,表示>=0.0.0的任意版本
version1 - version2
大于等于version1,小于等于version2
如:1.1.2 - 1.3.1,表示包括1.1.2和1.3.1以及他们件的任意版本
range1 || range2
满足range1或者满足range2,可以多个范围
如:<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0,表示满足这3个范围的版本都可以
更多的相关信息可以参考 语义化版本控制规范(SemVer)
2.2.3 版本升级规则
npm采用npm version
命令管理版本号
查看命令详情
npm version --help
结果:
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]
(run in package dir)
'npm -v' or 'npm --version' to print npm version (6.14.10)
'npm view <pkg> version' to view a package's published version
'npm ls' to inspect current package/dependency versions
prerelease
执行npm version prerelease
时,如果没有预发布号,则增加minor,同时prerelease 设为0;如果有prerelease, 则prerelease 增加1
npm version prerelease
0.1.0 -> 0.1.1-0
npm version prerelease
0.1.1-0 -> 0.1.1-1
那么alpha和beta版本是如何生成的呢?
可以借助 --preid
设置id,比如alpha、beta
npm version prerelease --preid=alpha
1.0.0 -> 1.0.1-alpha.0
npm version prerelease --preid=alpha
1.0.1-alpha.0 -> 1.0.1-alpha.1
npm version prerelease --preid=beta
1.0.1-alpha.1 -> 1.0.1-beta.0
npm version prerelease --preid=beta
1.0.1-beta.0 -> 1.0.1-beta.1
prepatch
执行npm version prepatch
,直接升级patch,增加预发布号为0
npm version prepatch
0.1.1-1 -> 0.1.2-0
npm version prepatch
0.1.2-0 -> 0.1.3-0
preminor
执行npm version preminor
,直接升级minor,patch置为0,增加预发布号为0
npm version preminor
0.1.3-0 -> 0.2.0-0
npm version preminor
0.2.0-0 -> 0.3.0-0
premajor
执行npm version premajor
,直接升级major,minor、patch置为0,增加预发布号为0
npm version premajor
0.3.0-0 -> 1.0.0-0
npm version premajor
1.0.0 -> 2.0.0-0
patch
执行npm version patch
,如果有prerelease ,则去掉prerelease ,其他保持不变;
如果没有prerelease ,则升级patch
npm version patch
2.0.0-0 -> 2.0.0
npm version patch
2.0.0 -> 2.0.1
minor
执行npm version minor
,如果没有prerelease,直接升级minor, 同时patch设置为0;如果有prerelease, 首先需要去掉prerelease;如果patch为0,则不升级minor;如果patch不为0, 则升级minor,同时patch设为0
npm version minor
2.0.1 -> 2.1.0
npm version minor
2.1.0 -> 2.2.0
npm version minor
2.3.0-0 -> 2.3.0
npm version minor
2.3.1-0 -> 2.4.0
major
执行npm version major
,如果没有prelease,则直接升级major,其他位都置为0;如果有prerelease, 首先需要去掉prerelease;minor和patch都为0,则不升级major,只将prerelease 去掉;minor和patch有任意一个不是0,则升级一位major,其他位都置为0,并去掉prerelease
npm version minor
2.4.0 -> 3.0.0
npm version minor
2.4.0-0 -> 3.0.0
npm version minor
3.0.0-1 -> 3.0.0
2.3 standard-version
standard-vrsion 是Conventional Commits.团队创建和维护的项目。基于semver ,并且能够自动生成变更日志的版本控制应用工具。使用standard-version,要求遵循Conventional Commits Specification约定提交规范。前面我们已经讨论和实现了约定提交规范。所以这里只需要安装、配置,即可直接使用standard-version管理版本控制和生成CHANGE LOG.
2.3.1 安装standard-version
npm i --save-dev standard-version
配置package.json
scripts
{
"scripts": {
"release": "standard-version"
}
}
执行命令
npm run release
结果:
✔ bumping version in package.json from 2.2.0 to 2.2.1
✔ bumping version in package-lock.json from 2.2.0 to 2.2.1
✔ outputting changes to CHANGELOG.md
✔ committing package-lock.json and package.json and CHANGELOG.md
✔ tagging release v2.2.1
从结果可以看出,standard-version release
命令执行了以下步骤
- 升级package.json中的version版本号
- 升级package-lock.json中的version版本号
- 自动生成日志到 CHANGELOG.md
- git commit package-lock.json、package.json 和 CHANGELOG.md文件
- 执行 git tag 打tag
如果一切准备就绪,就可以执行命令推送到远程git 库了
git push --follow-tags origin maste
2.3.2 standard-version常用关键参数
--first-release
首次应用standard-version,并不打算升级版本号,可以使用--first-release参数。仅仅打tag,但不会升级版本号。
standard-version --first-release
// 或者
npm run release -- --first-release
--prerelease
类似于npm version prerelease,基于--prerelease参数,可以生成预发布版本。
npm run release -- --prefrelease
// 1.0.0 -> 1.0.1-0
同样的,可以指定预发布版本的名称
npm run release -- --prefrelease alpha
// 1.0.0 -> 1.0.1-alpha.0
--release-as
如果你想升级major、minor、patch等版本号,可以使用--release-as
参数
# npm run script
npm run release -- --release-as minor
# Or
npm run release -- --release-as 1.1.0
基于上述两个参数,可以配置各版本升级命令
{
"scripts":{
"release":"standard-version",
"release:major":"standard-version --relase-as major",
"release:minor":"standard-version --relase-as minor",
"release:patch":"standard-version --relase-as patch"
}
}
--dry-run
如果你对升级版本没有把握,可以使用--dry-run
参数预览命令的执行结果,以及对文件的影响
# npm run script
npm run release -- --dry-run
# or global bin
standard-version --dry-run
执行过程如下所示:
✔ bumping version in package.json from 2.2.0 to 2.2.1
✔ bumping version in package-lock.json from 2.2.0 to 2.2.1
✔ outputting changes to CHANGELOG.md
---
### [2.2.1](///compare/v2.2.0...v2.2.1) (2022-03-24)
---
✔ committing package-lock.json and package.json and CHANGELOG.md
✔ tagging release v2.2.1
ℹ Run `git push --follow-tags origin master` to publish
✨ Done in 0.56s.
--skip
使用--skip参数,可以跳过 standard-version
命令的某个默认行为,例如, changelog
standard-version --skip.[option] [false/true]
option选项:
bump 缓存变化,并重置 git 状态至最近的 tag 节点
changelog 自动产出 changelog 文档
commit 提交变动
tag 在 git 中增加 tag 标识
2.3.2 自定义sandard-version配置信息
自定义配置方式
- 在
package.json
文件内进行配置 - 新建
.versionrc
配置文件进行配置(配置文件可以为.versionrc
,.versionrc.json
或.versionrc.js
)
主要配置项可以参考下面这张表:
我们以.versionrc.js配置文件为例,配置信息如下:
module.exports = {
header: '# 项目变更日志 \n\n',
types: [
{ type: 'feat', section: '✨ Features | 新功能' },
{ type: 'fix', section: '🐛 Bug Fixes | Bug 修复' },
{ type: 'init', section: '🎉 Init | 初始化' },
{ type: 'docs', section: '✏️ Documentation | 文档' },
{ type: 'style', section: '💄 Styles | 风格' },
{ type: 'refactor', section: '♻️ Code Refactoring | 代码重构' },
{ type: 'perf', section: '⚡ Performance Improvements | 性能优化' },
{ type: 'test', section: '✅ Tests | 测试' },
{ type: 'revert', section: '⏪ Revert | 回退' },
{ type: 'build', section: '📦 Build System | 打包构建' },
{ type: 'chore', section: '🚀 Chore | 构建/工程依赖/工具' },
{ type: 'ci', section: '👷 Continuous Integration | CI 配置' }
]
}
产出日志信息将是下面这个样子:
# 项目变更日志
### [0.0.9](https://gitee.com/muyi0327/react-ts-shop/compare/v0.0.8...v0.0.9) (2022-03-23)
### 🐛 Bug Fixes | Bug 修复
* **changelog:** 调整cz-config配置添加emoji ([3552ed4](https://gitee.com/muyi0327/react-ts-shop/commit/3552ed42c165562bacc0970e15fbc1760ea036ca))
### [0.0.8](https://gitee.com/muyi0327/react-ts-shop/compare/v0.0.7...v0.0.8) (2022-03-23)
### 📦 Build System | 打包构建
* **build:** 调整日志工具 ([d65654c](https://gitee.com/muyi0327/react-ts-shop/commit/d65654c1705c3d8ab9dc6a068e0e85cbfff40b67))
六、题后
有关规范部分的内容,暂且先讨论到这里。下一篇文章,我们将继续讨论以下话题:
- 样式管理规范与最佳实践
- 数据管理最佳实践
- 组件开发与管理
- 路由管理与实践
参考
www.ruanyifeng.com/blog/2016/0…
转载自:https://juejin.cn/post/7267879720577663015