注意事项#
本文默认开发环境是 VSCode,团队内尽可能统一开发环境,避免编辑器差异、插件不兼容的问题。
以下提到的工具核心都是配置文件,并且这些配置文件通常都能通过多种格式配置,例如 .js
、.yaml
、.json
或者直接在 package.json
中配置。后面不再赘述相关设定,具体配置形式可以在官方文档查询,本文仅解析部分配置项目。
代码风格#
代码风格一致是项目协作的基石,使用 ESLint 和 Prettier 可以避免由于代码格式不一致带来的代码合并冲突,也可以提高代码可读性和可维护性。虽然在认识 ESLint 和 Prettier提到过,但是下面想要作为升级版讲得更完整一点。
ESlint#
本文介绍的第一个工具是 ESlint,其功能是:
- 提供代码规范,例如:建议你使用
===
而不是==
,建议你未修改过的变量使用const
而不是let
等这样的规则 - 其次是格式化代码,控制缩进换行之类的问题
安装#
npm install --save-dev eslint
VSCode 插件#
在项目中安装了 ESlint 确实提供了文件校验和整理的接口,但实际上在写代码过程中格式化总不能每次都自己调一下 api 处理当前文件,这个时候就需要使用 ESlint 插件。安装插件后可以通过快捷键调用 eslint api 处理当前文件,甚至在保存时自动格式化当前文件,在编码窗口也会使用黄线和红线标注警告和错误代码。
安装完 ESlint 本体和 VSCode 插件你可以使用一些基本功能,但要与 React、Vue、TS 等文件配合使用需要安装相应插件(注意这里说的是 ESlint 的 plugins,跟前面提到的 VSCode 插件没有关系),后面会详细介绍插件(plugins)相关问题。
配置#
先看一眼 ESlint 的配置文件,它用于配置和扩充 ESLint 规则,这是一个例子:
{
"root": true,
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": { "project": ["./tsconfig.json"] },
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/strict-boolean-expressions": [
2,
{
"allowString": false,
"allowNumber": false
}
]
},
"ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"]
}
接着详细解析一下 extends、plugins、rules 三个配置。
Extends#
上面提到的规则有一两百条,要每条手动配置不现实,我们可以使用 Extends。
Extends 是配置的集合,添加了 Extends 等于添加了一组配置。配置 extends 的值时可以忽略包名的 eslint-config-
前缀。
很多大厂都有自己的一套规范,例如前端代码规范领域著名的 airbnb,他们的配置文件是 eslint-config-airbnb,安装之后使用时只需要这么写:
{
"extends": "airbnb"
}
不过…… 其实个人建议用 eslint:recommended
或者 standard
而不是 airbnb
,因为 airbnb 实属管太多,例如 no-plusplus
和 no-underscore-dangle
正常使用并没有什么问题,他也给开了,跟原来的编码习惯差距比较大所以选择不用了 😂。
当然,配置集合还有其他选择,例如 eslint-config-alloy;你还可以发布自己的配置集合,其实这也是官方推荐的做法。
Plugins#
Plugins 比 Extends 更强劲,不止可以补充配置,更能新增 ESLint 自定义规则。配置 plugins 的值时可以忽略包名的 eslint-plugin-
前缀;因为 plugin 中也可以包含配置集合,使用 plugin 中的配置集合时可以使用plugin:包名/配置名
的格式,如 plugin/essential。
在安装 eslint-plugin-vue 之后可以这样添加插件,就能在 ESLint 中新增一大堆 Vue 的规则(注意只是新增规则,并未配置规则是否使用):
{
// ...
"extends": ["plugin:vue/essential"],
"plugins": ["vue"]
// ...
}
各种语言详细的配置这里就不一一赘述了,eslint-plugin-xxx
的文档一般会提供比较完善的帮助。
Rules#
在 extends 添加完规则集合之后,很可能还要根据自己习惯微调一些规则,这时候就可以在 rules
配置一些单条规则。
规则的等级有三种:
- "off" or 0 - turn the rule off
- "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code)
- "error" or 2 - turn the rule on as an error (exit code is 1 when triggered)
配置方式大概长这样(一些特殊规则会有其他配置项,可以在规则对应页面获取相关信息):
{
"plugins": ["plugin1"],
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"],
"plugin1/rule1": "error"
}
}
其他问题#
配置未生效:
在更新完 .eslintrc
却发现新配置在 VSCode 没有生效时,可以通过 ctrl shift P 然后选 Reload Window
快速重启 ESlint 插件。
VSCode 保存时自动格式化配置,修改 settings.json
:
// settings.json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
// "editor.formatOnSave": true,
}
Prettier#
Prettier 是一个现代化的代码格式化工具。相比 ESLint,它专注于代码格式化,可以处理多种语言,包括 JavaScript、CSS、SCSS、markdown、yaml 等。Prettier 补全了 ESLint 只处理 js 系文件的问题,但对于 js 这个两者皆可处理的交集,仍然需要一些额外的兼容操作。
安装#
npm install --save-dev --save-exact prettier
注意一定要加 --save-exact
,因为不同版本的 prettier 处理某些格式时会有差异,为了保证团队全员格式相同,必须统一 prettier 版本。
VSCode 插件#
原理跟 ESLint 一样,npm 安装只提供 api,安装 VSCode 插件才能在编辑器方便格式化。
配置#
By far the biggest reason for adopting Prettier is to stop all the ongoing debates over styles.
Prettier 为了让大家少在格式上争吵,只提供了少数配置项,这样大家只要在这几个项目中争吵就可以了(误)。下面这些是 Prettier 几乎全部配置,文件名为 .prettierrc
:
{
"arrowParens": "always",
"bracketSameLine": true,
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleAttributePerLine": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false
}
ESlint 兼容#
之前有在用 eslint 和 prettier 让跨 IDE 协作更舒服提到可以手动让 ESlint 和 Prettier 兼容,不过那只是因为使用不同编辑器方便同步的一种方法,这里再介绍一下用 ESlint 插件兼容的方法。
要完整使用 ESLint 接替 Prettier 的工作需要 eslint-plugin-prettier。它的原理是使用 eslint-config-prettier 仅关闭所有 prettier 相关规则,然后通过插件把 Prettier 的规则转到 ESlint 一起校验。要做到上述全部操作只需要下面一行配置:
{
"extends": ["plugin:prettier/recommended"]
}
因为配置 "extends": ["plugin:prettier/recommended"]
后相当于填充了以下一组配置:
{
"extends": ["prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"arrow-body-style": "off",
"prefer-arrow-callback": "off"
}
}
其他问题#
特殊规则:
注意 prettier 覆盖了的一些特殊规则,例如会默认关掉 vue/html-self-closing
等规则,请再另外根据自己需求再 rules
配置。
提交门禁#
以上是统一代码格式相关的工具,但没有保证提交到代码库的代码经过格式化,使用 husky 可以在提交前对代码检查,保存提交的代码符合团队规范。
Husky#
Git hooks made easy 🐶 woof!
husky 为 npm 项目提供介入 Git hook 的能力,它支持所有 Git hook,但我们一般只会用到 pre-commit
和 commit-msg
。
pre-commit
用于提交前的检查,commit-msg
用于提交信息检测。
# 安装 husky
npm install husky --save-dev
# 启用 Git hook
npx husky install
# 配置 npm 的 prepare 命令,用于依赖安装后自动运行特定脚本
# 注意,使用 pkg 需要 npm 7 以上版本
npm pkg set scripts.prepare="husky install"
无法使用 pkg
可以直接在 package.json
配置:
{
"scripts": {
"prepare": "husky install"
}
}
之后使用 husky add <file> [cmd]
的格式添加 hook 触发的命令即可,接着介绍一下在 pre-commit
和 commit-msg
分别要使用的 lint-staged
和 commitlint
。
lint-staged#
Run linters against staged git files and don't let 💩 slip into your code base!
对于新接入 ESLint 的代码库,每次提交都检测所有文件,不通过就不允许提交的话,是不是有点过分了?使用 lint-staged 可以只校验当前提交的文件,让你的项目渐进式更新代码风格。
安装#
npm install --save-dev lint-staged
配置#
配置文件名 .lintstagedrc
,因为 lint-staged 的配置比较简短,可以直接写在 package.json
的 lint-staged
对象里。
配置范例:
{
"src/**/*.{ts,js}": ["eslint --cache --fix"],
"src/**/*.{json,less}": ["prettier --write"]
}
husky 触发#
# 在已安装 husky 的前提下运行
npx husky add .husky/pre-commit "npx lint-staged"
commitlint#
commitlint 可以检测提交信息是否符合规范。
简单来说就是这样的格式:type(scope?): subject
,实际例子可能是这样的:feat(blog): add comment section
。详细规范可以查看理解语义化 Commit。
安装#
npm install --save-dev @commitlint/config-conventional @commitlint/cli
配置#
配置文件名 .commitlintrc
,配置范例:
{
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // 新功能(feature)
'fix', // 修补bug
'docs', // 文档(documentation)
'style', // 格式(不影响代码运行的变动)
'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
'test', // 增加测试
'revert', // 回滚
'chore', // 构建过程或辅助工具的变动
'perf', // 性能优化
'types' // typescript类型定义文件更改
]
],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 72]
}
}
可以在 Github 查看更完整的填写示例。
husky 触发#
# 在已安装 husky 的前提下运行
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
包管理器#
使用固定包管理器,一般选择有以下几种:
想快速了解几种管理器的差异可以查看《速通 npm、yarn、pnpm》。同时强烈推荐《JavaScript package managers compared: npm, Yarn, or pnpm?》,讲得全面且清楚,下面贴一张来自此文的性能对比图:
可以按照喜好和兼容性自行选择,但是无论选择哪个管理器,非必要时,不要删除 lock 文件!!! 在安装依赖时,管理器需要计算依赖版本,很耗时,有 lock 可以直接按 lock 列表安装。
但是有 lock 的时候,依赖的下载地址是固定的,所以配仓库会不生效,持续集成时需要注意。
P.S. 不同管理器在配 husky 的时候可能有些差异
引擎版本#
如果保留了依赖的 lock 文件,那么记录当前应用适配的 node 版本也是十分必要的,不同 node 版本会造成下载的依赖不一样、甚至依赖根本不适配当前 node 版本;另外,还有一些老项目,必须用旧的 node 版本才能运行,但是接手的时候根本不知道用的那个版本,就十分让人无奈。
为解决这个问题,我们可以在 package.json
指定 node 和 npm 版本:
{
"engines": {
"node": ">=0.10.3 <15",
"npm": "~1.0.20"
}
}
团队人多、项目多时,很可能出现大家版本不一样的情况,在必要时,可以使用 node 版本管理器,这里推荐三款:
- nvm unix, macOS nvm-windows Windows
- n macOS, Linux
- fnm macOS, Windows, Linux
基本上可以做到一行命令安装版本、一行命令切换版本,十分方便。
.npmrc#
相信文件名带着 rc
已经很熟悉了,.npmrc
作用就是添加项目级别的 npm 配置,例如:
- 改仓库,有的公司有自己的镜像,可以在
.npmrc
配置,不用每次都在安装的时候带一串参数,也不会影响全局配置 - 还可以添加一些二进制包的下载地址,例如那个烦人的 sass
registry=https://mirrors.huaweicloud.com/repository/npm/
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
分支策略#
选择一种分支策略,可以选择以下三种策略,也可以以这些策略为基础调整出一个最适合自己团队的策略:
总结#
- 使用 ESlint 和 Prettier 控制代码规范和代码风格
- 添加 VSCode 插件极速格式化当前文件
- 添加提交门禁,保证上传到版本控制的代码符合规范
- 使用 husky 添加提交钩子,控制提交是否成功
- lint-staged 用于只检查本次提交文件是否符合要求,利于项目渐进式控制代码规范
- commitlint 保证提交信息可读性
- 团队内使用统一的包管理器,保留依赖 lock 文件
- 固定引擎版本,防止依赖变化,也能避免老项目不知道使用什么版本运行的窘境
- 使用
.npmrc
固定仓库等配置 - 选择一种分支策略,使代码历史更有序
各位看完觉得有遗漏或者有什么不懂我再补充!