likes
comments
collection
share

基于vite+react+typescript前端开发工程化(二)—— 开发规范

作者站长头像
站长
· 阅读数 20

导读

  • 基于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

基于vite+react+typescript前端开发工程化(二)—— 开发规范

然后,根目录下创建.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支持很多编程语言,下面这张图可供参考

基于vite+react+typescript前端开发工程化(二)—— 开发规范

Prettier支持很多编辑器,下面这张图可供参考

基于vite+react+typescript前端开发工程化(二)—— 开发规范

下面我们讨论一下opinionated,很多框架宣称自己是unopinionated框架,例如,express,react,vue。也有很多框架是opinionated框架,例如,Angular。

opinionated,原则是按照约定规则严格执行;而unopinionated相对比较自由。opinionated字面的意思约定的,因此Prettier是opinionated会忽略所有原有编代码格式,只保留约定规则。Prettier格式化过程是这样的,首先固有己见的先把你的代码转换成一种中间状态,叫 AST(Abstract Syntax Tree),然后按照自己的规则,统一格式化代码。

下图中,左侧是源代码,中间是AST,右侧是格式化后的代码。如果有兴趣,可以去prettier playground去尝试体验一下。

基于vite+react+typescript前端开发工程化(二)—— 开发规范

更详细的介绍可以参考官网。下面我们一起看一下的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

基于vite+react+typescript前端开发工程化(二)—— 开发规范

  • 修改编辑起配置文件settings.json,或者在项目更目录下面创建.vscode/settings.json文件。添加如下内容,这样修改保存文件时,Prettier将自动格式化代码。
{
    "editor.formatOnSave": true,
    "editor.formatOnType": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode"
}

常用编辑器对应的插件

常见的编辑器对应插件名
VS CodePrettier - Code formatter
Emacsprettier-emacs
Vimvim-prettier
Sublime TextJsPrettier
Atomprettier-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插件

基于vite+react+typescript前端开发工程化(二)—— 开发规范

  • 配置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-standardstylelint-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插件

基于vite+react+typescript前端开发工程化(二)—— 开发规范

编辑.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有相应的推荐工作流程:

  1. 提交修改信息 commit change
  2. 修改package.json中的version
  3. 执行npm run changelog生成日志
  4. 提交package.json和Changelog.md文件
  5. 打tag
  6. 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配置信息

自定义配置方式

  1. 在 package.json 文件内进行配置
  2. 新建 .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))

六、题后

有关规范部分的内容,暂且先讨论到这里。下一篇文章,我们将继续讨论以下话题:

  1. 样式管理规范与最佳实践
  2. 数据管理最佳实践
  3. 组件开发与管理
  4. 路由管理与实践

参考

chengpeiquan.com/article/edi…

www.ruanyifeng.com/blog/2016/0…

cloud.tencent.com/developer/a…

www.npmjs.com/package/sta…

转载自:https://juejin.cn/post/7267879720577663015
评论
请登录