管控前端代码质量,必要配置一览
- Published On
- Updated On
最前
本文涉及配置项一览表
配置项(文件(目录)/扩展等) | 作用概述 |
---|---|
.editorconfig | 跨编辑器平台的格式化规则约定 editorconfig |
.prettierrc.js | 简洁格式化工具 prettier |
.eslintrc.js | JS 等 JS 变体语言(TS,JSX,TSX)格式化工具 eslint |
.husky | 介入 git hook husky |
.lintstagedrc.js | 可关联 gitHook#pre-commit ,代码提交门禁 lint-staged |
.commitlintrc.js | 可关联 gitHook#commit-msg ,代码提交门禁 commitlint |
扩展:Prettier - Code formatter | vscode 生态插件,配合 prettier 配置使用 扩展:Prettier |
扩展:ESLint | vscode 生态插件,配合 eslint 配置使用 扩展:ESLint |
.stylelintrc.js | 样式表文件(CSS / SCSS / LESS)格式化工具 stylelint |
tsconfig.json | 配置 prettier、 eslint 格式化生效范围 |
setting.json | 以 vscode 编辑器为例,.vscode/ 目录下配置文件 |
特别说明
附上该表格在这里,意在说明,本文所述/罗列相关配置的简要分享,是从吾辈日常工作,及实际项目配置出发,比如相关配置是从项目整体背景是 React + TypeScript 出发,所以不可避免的存在一定片面性、局限性。权威的配置应直接跳转官网索引参考。
背景
前端工程化
前端工程化,网络上有着各家的理解和阐述,这里不再无脑 CV 贴到这里,但大致肯定都契合着这样一个主题:所谓工程化,一定是从(业务)项目本身的健壮性和可长期维护性出发。也不局限于开发代码本身,将“工程化”概念抬升一点,实际开发前的需求评审,开发测试完成后,CI/CD/打包部署,都可以看作工程化可涉及的要点。
而这当中,对代码本身、git 提交作规范限制,也是尤其重要的一环。
规范化一环
不言而喻的,如果是独立开发,代码天马行空的来都行,甚至于最原始的目的:能跑能上线也 OK。
但是立足于团队配合的代码开发项目,严格的规范限制、统一的代码风格、语义化 Commit 提交记录,才能让代码无论随着团队的成员迭代,人去人留,项目本身始终保持着其长期可维护性。
代码风格/偏好的约定
显然的,不同的开发从业人员大致有着不同的代码风格偏好,比如这么个浅显的问题,“在前端代码里,究竟该不该使用分号”。
这个问题下,如果我们共同遵守一套规范,规范限制了代码使用强分号风格,不用也用。而规范声明应使用 JS 下支持的无分号风格,用也不用。
总而言之,开发人员约定并遵守相同的规范,并在开发过程中辅以 Lint 格式化工具,长远受益。
正题
1. 跨不同编辑器平台的统一格式化约定
虽然本文主要从吾辈日常使用的编辑器 vscode 出发,但是我们依然有手段实现跨不同编辑器平台的统一格式化约定 —— 尝试使用 EditorConfig 配置文件
editor config
一些技术分享博文略去了该配置文件,诚然,其声明的约定格式都可以通过后续的 eslint 或者 prettier 来配置实现。
不过吾辈抱持着这样一个观点,只要靶向的是同一个功能需求,能够接受多个不同配置间是可以冗余重复的,只要合理配置,不让其相互冲突就行,另个角度来说,也是种兜底措施,一个配置出了问题,或者放弃不用,还有另个配置支持着相同功能。
一个典型的 editor config 配置文件内容如下:
# https://editorconfig.org/
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
具体的配置含义不作赘述。
vscode 编辑器下,可配合编辑器扩展EditorConfig for VS Code 使用。
2. 格式化流行工具
当下流行的两个格式化工具: prettier 和 eslint;
- 前者能够支持对各种格式文件作简单格式化需求,但是可以说只能做“死板的格式化”,不具备代码逻辑/合法性校验功能。
- 后者聚焦于对 JS、TS 等变体语言的代码逻辑/合法性校验,及一定的自动修正功能,同时可以取代 prettier,作格式化功能。
配置文件格式选择
通常的,配置文件支持多种格式,比如广泛使用的 json
格式,以及 js
、yaml
,甚至于直接将配置内容书写在 package.json
文件下。
使用何种配置文件,看各家偏好。
吾辈的偏向来说,更偏向于/推荐使用 .js
(.cjs
, .mjs
) 后缀格式的配置文件,因为:
-
可以友好的添加一些注释备忘 (原始
json
文件无法做到这一点,当然json
本身就是为了去除注释,还配置文件一份清爽); -
对一些冗长的配置作单独文件导出;
(比如 Prettier 下,基于相关插件如 prettier-plugin-sort-imports 对
import
语句导入顺序的格式化配置声明)可以单独声明一个变量并导出,而后在主配置文件下导入。如下的例子:
而在 prettier
主配置文件下,则可以想这样导入:
而这是使用 json
格式作为配置文件做不到的,配置文件或许因为一些插件的具体配置声明,而显得过长(当然本身不是什么问题,还是看开发者个人喜好)
2.1 prettier
前文提到:prettier
能够支持对各种格式文件作简单格式化需求,包括不限于 JS、 CSS、 Markdown 等文件内容,但不具备代码逻辑/合法性校验功能。
其专注于代码的格式化。当然,同 eslint
两者都支持对 JS 等语言的格式化处理,所以两“工具”都有在同一项目中工作时,需注意兼容性处理。会在 eslint 章节提及对兼容性的配置细节。
prettier 也提供了丰富的插件生态,使用 prettier 插件时的相关细节应主要参考: prettier 官方和插件本身的官方说明页((以 prettier-plugin-sort-imports
为例))来作进一步的自定义配置。
prettier 依赖安装的版本应明确指定
prettier 不同版本下,可能存在格式化结果的不同,所以为了确保开发团队的格式化结果总是保持一致,package.json
文件下的版本声明、包管理器单独安装时都应对版本作明确指定:
pnpm add -D --save-exact prettier@2.8.8
辩证看待:你是否需要 prettier
不同的“工具”间,可能存在功能交叉的地方,比如:
prettier
和eslint
都可以处理 JS 等代码格式化;prettier
和stylelint
都可以处理样式表文件格式化;
一篇优秀的博文: Why I don't use Prettier 提出 prettier 的“死板格式化”可能给 git 版本控制带来的一些痛点。(能给出一定参考性)
另外,吾辈的观点,前面已经陈述过:“只要靶向的是同一个功能需求,吾辈能够接受多个不同配置间是可以冗余重复的,只要合理配置,不让其相互冲突就行”。
但如果开发者是另一种观点:少即是多(Less is More),其实舍弃 prettier
,全面拥抱 eslint
单独工作:代码合法性校验,以及格式化工作,也是可行并推荐方案。
概念理清:第三方库&编辑器扩展各自职责
这里有必要理清第三方库 prettier
和编辑器扩展(插件)prettier
两者的角色职能。
这里为了阅读区分,后文总是将编辑器扩展称为extension@[somename]
,而将第三方库称为lib@[somename]
。
首先,须知道,编辑器扩展extension@prettier
依赖于第三方库 lib@prettier
的前置存在, 当然 prettier 可以不安装在项目 node_modules/
下,而存在于 npm install -g
全局安装目录下。
但是 extension@prettier
要正常工作,底层是依赖于的lib@prettier
对外暴露的功能 api。
- 先来看没有
extension@prettier
而已经装上lib@prettier
的情况下:
此时如果我们需要对代码作格式化处理,需要借助 lib@prettier
提供的命令,如在命令行窗口如下的输入:
pnpm prettier --check --plugin-search-dir=. **/*.{ts,tsx,js,jsx,cjs,mjs,md,mdx,json,scss,less,yaml,yml} --ignore-path .prettierignore --ignore-unknown --no-error-on-unmatched-pattern
如上的命令来完成对指定目录下的文件做格式化处理。
哪怕将上面的命令声明在 packageJson#scripts
字段下,但显然的,日常开发中,不能总依赖命令行的手动输入。
这时就需要编辑器扩展来完成一些自动化工作。
- 已经装上
lib@prettier
的情况下,配合extension@prettier
扩展:
安装对应扩展后,就允许我们通过快捷键来调用 lib@prettier
提供的 api 来执行格式化动作、或者保存文件时自动格式化当前文件。
prettier 编辑器扩展
vscode 编辑器下,插件地址。
细节:虽然一般不会出现问题,但还是应注意安装同 lib@prettier
兼容版本。在其官网文档中有说明。
在编辑器本身配置文件 setting.json 的修改
前文提到:安装对应扩展 (extension@prettier
)后,就允许我们通过快捷键来调用 prettier api 来执行格式化动作、保存时自动格式化当前文件。但是需要我们对编辑器本身配置做一定配置,视开发者自身情况,确定修改的是编辑器全局配置文件,还是当前项目/用户配置文件:
其中,我们可以显式声明 prettier.configPath
字段,指定配置文件地址。
修改片段参考:
scripts 下声明相关脚本命令
突发的,如果extension@prettier
在 setting.json
文件中声明何时使用的配置错误,或扩展本身异常,导致该扩展本身未能正常工作,
我们除了依赖lib@prettier
提供的 api,在命令行窗口手动输入命令。
或着另个场景:对工程目录下的所有文件进行批量的格式化操作,都应该在 scripts 下声明相关脚本命令,参考如下:
则允许命令行中如下调用:
# 进行格式检验,但并不格式化处理
pnpm pt-format:check
# 格式化处理
pnpm pt-format
另,eslint 同样适用以上两点。后文同样给出参考代码片段。
2.2 eslint
eslint 聚焦于对 JS、TS 等语言的代码逻辑/合法性校验,及简单的修正功能,同时可以替代 prettier,作格式化功能。总之,在 JS 处理上功能更加强大。
其也提供了丰富的插件生态,使用 eslint 插件时的相关细节应主要参考: eslint 官方和插件本身的官方说明页(以 eslint-plugin-import
为例)来作进一步的自定义配置。
各类知名开源仓库下都有着各自完善的 eslint 配置文件直接拿来参考:
比如来自 Adobe 的 react-spectrum 项目的 eslint 配置文件
官方约定规则, config & plugin
首先,需说明,对于 eslint 生态下的 eslint-config-[somename]
及 eslint-plugin-[somename]
的使用细节,来自于官方的规则定义,且随着版本可能有所调整。
总的来说,eslint-plugin-[somename]
比 eslint-config-[somename]
提供更强大的功能:除了可以如同后者可以引入配置集合,也可以新增 ESLint 自定义规则
另外,当前引入规则,可以去除 eslint-plugin-
和 eslint-config-
这样的公共前缀,总之:声明规则上不确定的地方直接索引官网文档。
{
// ...
"extends": [
"eslint:recommended",
"plugin:react/recommended", // 直接引入来自 eslint-plugin-react 的“推荐的”配置集合
],
plugins: ["import"], // -> "import" 即 eslint-plugin-import | 该插件作用场景 - import 语句调序 | 与 prettier 下生态插件 `prettier-plugin-sort-imports` 作用有一定重叠
}
eslint 兼容 prettier | 接管 prettier 预警及报错
虽然我们可以不依赖 prettier , 完全依赖 eslint 本身完成格式化工作。但如果想要让两者:eslint + prettier 配合工作,需要在 eslint 配置文件下做必要配置。
首先,需要安装依赖如下
pnpm add -D eslint eslint-config-prettier eslint-plugin-prettier
而在配置文件中,最简洁的声明如下:
{
// ...
"extends": [
"eslint:recommended",
"plugin:prettier/recommended",
],
plugins: [/* ... */],
}
TIP: 如上的,也能说明当前 eslint 版本下,使用 eslint-plugin-[somename]
和 eslint-config-[somename]
插件的细节地方:
如上的,我们安装依赖时包含 eslint-config-prettier
,但是 eslint 配置文件里没有出现该插件名称,但实际仍然是使用了该插件的,因为该 extends - "plugin:prettier/recommended"
声明语句实际等效为如下 :
{
"extends": ["prettier"], // here: "prettier" -> eslint-config-prettier
"plugins": ["prettier"], // here: "prettier" -> eslint-plugin-prettier
"rules": {
"prettier/prettier": "error", // 🎈 turns on the rule provided by this plugin, which runs Prettier from within ESLint.
"arrow-body-style": "off",
"prefer-arrow-callback": "off"
}
}
更详细说明见: eslint-plugin-prettier Recommended Configuration。
当然,再次重复:以上这样的配置细节始终是 eslint 官方随版本迭代可能随时改变的使用规则,开发者只需遵守并按官网或插件的说明文档来作配置即可,无需过分关注。
接下来,来具体看看:eslint 接管 prettier 后预警及报错是如何体现的:
来看这样的截图示例:tailwind 原子类名排序格式化在 prettier 、 eslint 的配置文件中分别声明依赖于:
- prettier 插件:prettier-plugin-tailwindcss
- eslint 插件:eslint-plugin-tailwindcss
则在原子类排序“错误”情况下给出的预警/报错情况:
其中第一个红框预警来自:eslint-plugin-tailwindcss
, 第二个预警来自: prettier-plugin-tailwindcss
(原 prettier 预警报错由 eslint 接管后此时统一表示为 eslint(prettier/prettier)
;
另个图例: 对 import 语句排序格式化在 prettier 、 eslint 的配置文件中分别声明依赖于:
- prettier 插件:@ianvs/prettier-plugin-sort-imports
- eslint 插件:eslint-plugin-import
则在import 语句顺序错误情况下,给出的报错情况:
其中第一个红框预警来自:@ianvs/prettier-plugin-sort-imports
, 第二个预警来自: eslint-plugin-import
;
由两个截图示例可以直观看出,在 eslint 配置文件中声明了兼容/接管 prettier 格式化后,报警输出呈现 eslint(prettier/prettier)
这样的统一输出。
当然的,上面仅是为了说明eslint 接管 prettier 后 的代码校验预警报错的表现,实际 eslint 和 prettier 两者生态下功能相同或近似的插件, 选择其一安装即可。
所以上面提到的,不妨直接使用 eslint-plugin-tailwindcss
来管理原子类名排序;使用 eslint-plugin-import
来管理import
语句排序。无需冗余安装 prettier 生态下同类型插件。
eslint 编辑器扩展
vscode 编辑器下,插件地址。
同编辑器扩展 extension@prettier
的角色一样,如果不安装extension@eslint
,我们要使用 eslint 提供的强大语法校验及格式化功能,只能依赖其提供的命令行命令,大致形如:
eslint . --report-unused-disable-directives --fix
而extension@eslint
扩展就允许我们通过快捷键来调用 lib@eslint
提供的 api 来执行代码语法逻辑/合法性校验,(包括接管 prettier 后、及其他各插件引入的一众格式化配置)格式化动作、或者保存文件时自动格式化当前文件。
也是该扩展extension@eslint
为我们带来了在编辑器窗口,实时的代码行底红色波浪线报错、黄色波浪线预警。
在编辑器本身配置文件 setting.json 的修改
修改片段参考:
scripts 下声明相关脚本命令
以及,对工程目录下的所有文件进行批量的 eslint 格式化操作,可在 scripts 下声明相关脚本命令,如下参考:
对 JS/TS 两套“独立”的 eslint 配置
本小节依然是对 eslint 配置细节的展开:TypeScript 技术栈开发下,我们有必要引入 @typescript-eslint/eslint-plugin 这样的配置规则,按照最佳实践来规范 TS 代码。
pnpm add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript
但是如果按照 @typescript-eslint/eslint-plugin 的初始文档说明操作,可能会全盘将 JS/TS 等格式文件都交由 @typescript-eslint/parser
、 @typescript-eslint/eslint-plugin
作校验处理了。
而开发者或许并不需要如同对 .ts
文件同级别的校验规则对 .js
文件处理,所以我们可以使用 eslint 配置文件下提供的 overrides
字段,
则 eslint 配置文件大致形如:
喘口气,歇息一下 ~
3. 提交门禁
前文所述,都是聚焦于代码书写环节,实现规范化的相关配置,但团队协作下,我们还需将完成无误的代码提交到指定远程仓库,而在提交这一环节,同样有必要引入相关规范或者说是限制。
husky 扮演角色
这当中,husky 为我们提供了可靠的介入 git hook 的功能。
# 安装 husky
pnpm add -D husky
# 启用 Git hook
npx husky install
# 配置 prepare 命令,用于依赖安装后自动运行特定脚本
# 注意,使用 pkg 需要 npm 7 以上版本
pnpm pkg set scripts.prepare="husky install"
在提交这一环节,我们可使用 husky
介入 git-hook,多数情况下,我们主要处理 pre-commit
和commit-msg
钩子,前者可用于提交前对代码进行合法性的最终检查,后者见名知义,是对提交信息本身的规范性检查。
lint-staged | 渐进式引入 lint
前文提及,可以使用 packageJson#scripts
下声明的 pnpm lint:fix
对全局代码做代码合法性校验及格式化处理。
但现实情况里,如果我们接触到的是半途引入 eslint 的项目,则力有所逮,更应考虑渐进式处理,只 lint 校验格式化本次提交的代码。这就可以使用到依赖 lint-staged,其使用场景:
Linting makes more sense when run before committing your code. By doing so you can ensure no errors go into the repository and enforce code style. But running a lint process on a whole project is slow, and linting results can be irrelevant. Ultimately you only want to lint files that will be committed.
pnpm add -D lint-staged
安装好该依赖后,配置文件初始化示例:
pre-commit hook 触发,关联 lint-staged 配置文件
初始化 husky 介入 pre-commit
钩子,执行 npx lint-staged
npx husky add .husky/pre-commit "npx lint-staged"
commit lint
对 git commit msg
提交信息的规范,我们可安装依赖 @commitlint
pnpm add -D @commitlint/cli @commitlint/config-conventional
简单的配置文件示例:
或者跳转官方参考实例
commit-msg hook 触发,关联 @commitlint 配置文件
初始化 husky 介入 commit-msg
钩子,执行 npx commitlint
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
引擎版本
一般而言,多个独立项目间因为新旧原因,创建时间的不同,或许 node 依赖版本存在差异,建议在 packageJson
文件下显式声明当前项目所用的 node 版本(也可包含使用的包管理器的版本),便于接手项目的开发人员能够顺利的在对应 node 版本下,将项目运行起来。
{
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"packageManager": "pnpm@8.6.11",
}
总结
全文冗长,却仍然片面,更全面的/整体的关于代码质量主题推荐阅读该开发者博文:前端代码质量与团队协作终极指南 👍
下面也对本文全篇涉及要点做个简要总结:
- 使用
editor config
完成跨编辑器平台的格式化约定; - 使用
prettier
做基本格式化约定; 也可略去prettier
使用,全面拥抱eslint
; - 使用
eslint
处理 JS 、TS 文件的代码合法性校验及格式化工作; 如果协同 prettier 格式化,则需做好兼容性处理,完整接管 prettier 格式化/预警报错规则。 - 安装 eslint 、 prettier 配套的 vscode 扩展,让代码合法性校验及格式化工作更加便捷。
- 使用
husky
介入 gitHook,实现提交门禁。 - 依赖
lint-staged
,处理提交时的:提交代码的合法性检查。 - 依赖
@commitlint
,处理提交时的:提交信息的规范性检查。 - 显式声明引擎版本,利于接手维护。
延伸
ignore 文件下的“反选”
一般的,各配置文件都有着自己配套的 ignore
文件,过滤掉指定文件群。比如:
- eslint 下:
.eslintignore
文件。 - prettier 下:
.prettierignore
文件。
但如果有对:比如配置文件本身、或其他 eslint、 prettier 原本默认过滤的文件,也有格式化的需求:则可以如下反选声明:
# 约定 | 为了对如下的几个配置文件也适用 prettier 的约定客制化格式
!.eslintrc.cjs
!.prettierrc.cjs
!.lintstagedrc.cjs
!.commitlintrc.cjs
为项目声明一份“推荐扩展”
以编辑器 vscode 为例,在编辑器本身的 .vscode/
配置目录下,除了推荐同步向远程仓库的 settings.json
文件。我们也可以初始化并同步 extensions.json
文件,旨在声明该项目推荐/或必要安装的扩展清单:
参考例:
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"bradlc.vscode-tailwindcss",
"unifiedjs.vscode-mdx",
"yoavbls.pretty-ts-errors",
"editorconfig.editorconfig",
]
}
显示效果如下:
配置独立发包
可以考虑对项目下 eslint 的配置文件二次封装为 npm 包,增强不同项目间的复用性。如下的风格参考:
关注编辑器扩展是否正常工作
该问题的具体举例:比如:settings.json
下配置了文件保存时即刻使用 prettier 格式化当前文件, ——但是发现并未生效。
(或者生效了,但实际是 extension@eslint
接管了 extension@prettier
的格式化工作,所以保存文件动作下,extension@prettier
扩展实际一直处于静默状态(?存疑)。我们可以手动调用 extension@prettier
,尝试让其工作 :使用 vscode 的 Ctrl+Shift+P
呼出菜单下的Format Document
菜单项,如此操作下表现在 vscode 编辑器界面上,就是底部状态栏的扩展 prettier
一栏被高亮标红,点击可查看该扩展的报错信息)
此时不一定是自己的配置出了问题,而可能就简单的是: lib@prettier
版本,和 extension@prettier
的版本存在不兼容问题,也就导致了在 settting.json
下声明的extension@prettier
相关配置最终失效。
配置未生效或仍然是旧有配置
某些情况下,如果 eslint / prettier 的配置未生效或校验及格式化使用的仍然是旧有配置,可尝试 Reload Window
命令重启编辑器窗口,(重启extension@eslint
、extension@prettier
等扩展)
THX
😘 强烈的爱与支持来自瓷器: 前端代码质量与团队协作终极指南
管控前端代码质量,必要配置一览
https://blog.ninoh.cc/blog/a6-frontend-code-quality[Copy]转载或引用本文时请遵守“署名-非商业性使用-相同方式共享 4.0 国际”许可协议,注明出处、不得用于商业用途!分发衍生作品时必须采用相同的许可协议。