团队协作(一)—— 你不知道的 ESLint + Prettier


theme: condensed-night-purple

参考:

  • https://eslint.org/
  • https://prettier.io/
  • https://prettier.io/docs/en/related-projects.html
  • https://segmentfault.com/a/1190000020835739
  • https://www.robinwieruch.de/prettier-eslint/
  • https://drylint.com/projectConfig/eslint/
  • https://vueschool.io/articles/vuejs-tutorials/eslint-and-prettier-with-vite-and-vue-js-3/

一、前言

这是团队协作系列的第一篇,字数 8000+,长文警告 ⚠

有经验的工程师应该了解,开发一个项目前我们需要一份团队开发规范,以此为共识,共同构建我们的项目开发。

去年公司的一个项目需要重构,为什么需要重构?除了产品设计发生变化以外,前端的 UI 样式发生了大变革。产品设计的变化意味着功能的变动,我们要对功能代码进行调整;UI 的大变革体现在高度定制化,我们需要构建一个自己的组件库。作为团队新人,在我看到项目代码,尤其让我印象深刻的是,核心代码无注释、ES6 使用较少、变量命名无章法、文件结构组织混乱、组件封装性有待提高…… 以上想必也是大部分工程师面临的困境……

需要做的事情很多,但古语有云:“三军未动,粮草先行”。面对上述诸多问题,我查阅了很多资料、与很多大佬交流,起草并拉着团队成员制定了第一版前端开发规范,再拉会宣讲、统一团队认知,对内在开发规范层面达成共识,对外展现我们是会思考、有保障的工程师。

本着“取其精华,去其糟粕”的原则,在进行开发工作的过程中,我们对规范进行了适当补充和删减,不断反馈和完善,当然过程非常艰辛。就这样,在大家的共同努力下,代码质量得到了显著提升。

1.1 文档类规范的局限性

在这个项目上的试验成功以后,我准备将这套规范推广到更多项目,正巧那时我已开始负责公司所有的前端开发管理工作。但是遇到了很多问题,其中明显感觉心有余而力不足的一点是,随着项目需要团队开始扩充人数,然而绝大多数都是实习生,这意味着我需要在实习生培养与不同项目开发之间达到动态平衡。实习生没有太多开发经验,对于开发规范的理解相对是有难度的,我想要通过 Code Review 来保证代码质量以及最佳实践的引导和传授。事实证明这很难,我不得不花费大量精力保证代码质量和风格,并且有时迫于紧张的工期压力,没有办法全身心地关注实习生的每一次 pull request,于是只好抓大放小。逐渐地,我发现即便是有文档,也很难实现代码风格的同步,散落在项目代码中的“不规范代码”在将来会产生巨大的隐患,比如:“make a shit mountain of code”。

1.2 团队组织的合作问题

1999 年 9 月 23 日,NASA 发射的火星探测器到达火星,准备执行任务,但与地面失联。按照原计划早就重新绕到火星前面,但地面无信号。最后发现是因为负责制造推进器的公司和负责制造中央处理器的公司采用了不同的单位,力学单位换算出错,最后引发了这场坠毁事故,可想而知这样的代价多么高昂……

NASA 曾经因为力学单位不一致遭受严重的损失。对于团队组织来说,如果开发规范不统一,意味着整体步调不一致,合作将受阻,最后引发一系列问题。为什么呢?站在 10 人以内团队管理经验的角度下,我认为至少存在以下 6 种可能性:

第一、存在不同成员需要开发同一个项目的可能性;

第二、存在不同成员需要维护不属于自己那部分代码的可能性;

第三、存在不同成员需要同时开发一个模块的可能性;

第四、存在不同成员需要同时兼顾不同项目开发的可能性;

第五、存在不同成员使用不同 IDE 的可能性;

第六、存在异地团队协同开发的可能性。

如果不统一开发规范,短期来看,好像开发得更加迅速,可以更快推出产品。但长期来看,以上六点叠加的,不管是技术上还是管理上,它们附带的显性的或者是隐患的成本都极其高。

1.3 使用工具的本质

所以,基于以上两大问题,如何统一开发规范就成了关键问题。ESLint + Prettier (+ editorconfig + husky + commitlint) 这套工具组合可以说是神技了,可以在一定程度上实现代码层面的自动化,至少可以完成团队层面的代码规范统一(代码逻辑仍然需要有经验的工程师进行 Code Review)。这种统一工作表面上是在增加开发人员的心智负担,看上去还和业务没有任何关系,但本质上却提升了代码质量,在测试之前就减少了大量的 bug,在测试之后,减少了代码维护成本和后续开发的认知成本。如此一来,让产品稳定上线,提升用户使用体验,为业务发展保驾护航。否则是在浪费时间,我依稀还记得那一次我花了 3 个多小时合并代码的窘境和痛苦……

在设置 ESLint 和 Prettier 之前,要知道它们是什么,我的概括如下:

ESLint 是什么?检测 JS 代码,发现代码质量问题并修复问题,还可以自己根据项目需要进行规则的自定义配置以及检查范围等等。

Prettier 是什么?代码格式化工具,可以让团队的代码风格保持一致。可支持的源码类型包括:JavaScript, JSX, Angular, Vue, Flow, TypeScript, CSS, HTML, JSON, YAML……

简单来说,前者用于代码潜在问题的发现,后者用于代码风格的保持。

我不准备根据官方文档娓娓道来,而是通过几个常用的脚手架工具直接让大家上手体验效果。

二、浅尝 Prettier

以 VSCode 为 IDE,需要安装好 Prettier 扩展插件。

  • Prettier: https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

团队协作(一)—— 你不知道的 ESLint + Prettier_第1张图片

2.1 设置:保存时格式化

在设置中搜索找到 Editor: Format On Save,勾选它:

团队协作(一)—— 你不知道的 ESLint + Prettier_第2张图片

勾选之后,通过 Ctrl+Shift+P 搜索找到 settings.json

团队协作(一)—— 你不知道的 ESLint + Prettier_第3张图片

点击进入后可以看到最下方新增了一条 VSCode 配置设定: "editor.formatOnSave": true,

团队协作(一)—— 你不知道的 ESLint + Prettier_第4张图片

设置完成后,当你在保存文件时,VSCode 就可以帮你进行格式化了。到这你一定有疑惑,VSCode 根据什么来格式化?是的,格式化的形式需要你自己选择。别担心,这个后面我会提到,go on。

2.2 在 VSCode 中使用 Prettier

打开设置找到 Prettier: Single Quote,勾选它:

团队协作(一)—— 你不知道的 ESLint + Prettier_第5张图片

同理, settings.json 新增了一条 VSCode 配置设定:"prettier.singleQuote": true,

团队协作(一)—— 你不知道的 ESLint + Prettier_第6张图片

在前言部分提到,Prettier 是用来保持代码风格的,使用单引号而非双引号就是一种代码风格。在勾选了这个设定以后。找个目录,新建一个 index.js:

javascript const express=require("express");

第一行中就用了双引号,当保存时就会应用 2.1 部分的自动格式化,将双引号修复为单引号。

现在按下保存键试试,会有通知提示你需要进行配置

团队协作(一)—— 你不知道的 ESLint + Prettier_第7张图片

点进去,选择 Prettier-Code formatter 即可 (其他前端相关的代码也选择 Prettier 插件即可)

团队协作(一)—— 你不知道的 ESLint + Prettier_第8张图片

基本可以猜到,只要是有关 VSCode 的配置都会在你设置后,让 settings.json 新增一条相关配置:

image.png

再次保存,你会发现修复好了。

团队协作(一)—— 你不知道的 ESLint + Prettier_第9张图片

注意,这里的修复不仅是引号问题被格式化修复,赋值号的前后添补了空格,原因在于 Prettier 作为 VSCode 的插件,它自带有一套默认的代码风格配置。

在哪里看配置?新建终端 ⇒ 左边找到【输出】tab ⇒ 右边找到 Prettier。

团队协作(一)—— 你不知道的 ESLint + Prettier_第10张图片

在上面的截图中,除了 Prettier Options,还有

  • 检测 ignore file( .prettierignore) 是否存在
  • 检测本地配置文件( .prettierrc or .editorconfig)是否存在

好了,此处又可以猜到检测的目的。如果既安装了插件又存在忽略文件或者配置文件,那么 VSCode 插件的权重则更低,也就是会被本地配置文件覆盖。

在 github 上验证了这个猜想是正确的:https://github.com/prettier/prettier-vscode#visual-studio-code-settings

团队协作(一)—— 你不知道的 ESLint + Prettier_第11张图片

从优先级可以看出,对于团队项目开发来说,使用 prettier configuration file 更为实际。另外,理解这个优先级是非常重要的。在项目中,如果你发现配置的和你预想的不一样,你就要想想看是不是这三处冲突了,因为他们是可以并存的。

三、为什么是 ESLint + Prettier

在前言中提到,ESLint 是用于发现并修复代码问题(包括代码风格格式化),这和 Prettier 美化代码风格显得很矛盾,因为它们存在重叠的部分,那么业界为什么要将二者结合在一起使用呢?

3.1 疑惑一

一开始我是很疑惑的,后来在 Prettier 的官方文档中找到如下解释,让我有点豁然开朗:https://prettier.io/docs/en/comparison.html

团队协作(一)—— 你不知道的 ESLint + Prettier_第12张图片

ESLint 对于代码问题的检测采用两类规则,一类是格式化规则,一类是代码质量规则。前者与 Prettier 做的事情是一致的,因此,我们通过 Prettier 执行格式化代码的工作,而代码质量的控制由 ESLint 处理。换句话说,前者美化代码,后者捕获 bugs。

3.2 疑惑二

那么问题又来了,既然 ESLint 可以把事情全都做了,为什么不直接用 ESLint?这个问题困扰我很久,我的字节朋友(强哥)给了我答案: ESLint 之类的 Linters 对于代码格式化的能力是有限的,不如 Prettier 那么专业,这是常见的二者冲突问题。

3.3 疑惑三

所以,怎么处理重叠部分的冲突?Prettier 提供了两个插件:

  • eslint-config-prettier

    • https://github.com/prettier/eslint-config-prettier
    • 解决 ESLint 中的样式规范(即代码风格)和 Prettier 中样式规范的冲突,以 Prettier 的样式规范为准,使 ESLint 中的样式规范自动失效。对应 .eslintrc 配置 extends-"prettier"
  • eslint-plugin-prettier

    • https://github.com/prettier/eslint-plugin-prettier
    • 将 Prettier 样式规范作为 ESLint 代码质量规范来使用,同样将格式问题以 error 的形式抛出,即 rule-"prettier/prettier",可在 rules-"prettier/prettier" 中进行自定义配置。对应 .eslintrc 配置 plugin-"prettier"

四、在 Vue 项目中的配置

仅以 Vue3 + TS 为例。

  • 在进行此步骤时,请在 settings.json 中把第二节的 Prettier 单引号格式化关闭,避免与项目设定产生冲突。

    image.png

  • 在 VSCode 中安装插件:Vue Language Features (Volar),给 Vue 提供代码高亮和语法提示。

  • 在 VSCode 中安装插件:ESLint,这样代码不规范的时候底部才有波浪线出现。

4.1 使用 create-vue 创建 Vue 项目

  • https://vuejs.org/guide/quick-start.html
  • https://github.com/vuejs/create-vue

打开命令行终端,按照官网提示,执行:npm init vue@latest

团队协作(一)—— 你不知道的 ESLint + Prettier_第13张图片

注意,最后两项(ESLint、Prettier)需要选上。

package.json 中能够看到 eslintprettier 的相关依赖已经被添加到开发依赖中:

团队协作(一)—— 你不知道的 ESLint + Prettier_第14张图片

打开项目中的 .eslintrc.cjs 文件(ESLint 配置文件):

```javascript /* eslint-env node */ require("@rushstack/eslint-patch/modern-module-resolution");

module.exports = { root: true, extends: [ "plugin:vue/vue3-essential", "eslint:recommended", "@vue/eslint-config-typescript/recommended", "@vue/eslint-config-prettier", ], parserOptions: { ecmaVersion: "latest", }, overrides: [ { files: ["cypress/e2e/**.{cy,spec}.{js,ts,jsx,tsx}"], extends: ["plugin:cypress/recommended"], }, ], }; ```

这是 create-vue 脚手架官方默认配置好的 eslint 配置文件,我们从这里启程。

可以看到导出的对象中有这么几个字段:

  • root
  • extends
  • parserOptions
  • overides

这些是 eslint 的配置项,分别来看看他们有什么作用。

4.1.1 root

javascript module.exports = { root: true, // others };

root:是否以当前目录为根目录。 告诉 ESLint 不要再往上级目录查找,利用此属性配置,项目级和目录级的配置都可以不受上级目录以及祖先目录的配置影响,通常项目根目录应该设置为 true

举个例子,比如这个项目 vue-project1,默认情况下 root 为 false,而且该项目上层目录下还有 eslint 配置文件的话,这个更上一层的配置就会对你的项目文件的代码产生作用,直到到达根目录才会停止。这是我们不愿意看到的,所以就需要我们在当前项目目录下设置 root: true,告诉 ESLint 这里就是根目录了,别再往上查找其他的配置文件了!

官网上还讲了 .eslintrc.* 文件或 package.json 文件中的 eslintConfig 字段在不同层级的作用关系,感兴趣的可以去看一下:https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy

4.1.2 extends && rules

javascript module.exports = { extends: [ "plugin:vue/vue3-essential", "eslint:recommended", "@vue/eslint-config-typescript/recommended", "@vue/eslint-config-prettier", ], // others }

https://eslint.org/docs/latest/user-guide/configuring/configuration-files

extends:ESLint 扩展配置。 ESLint 是根据一批规则(rules)来检验代码质量的,比如上面的 eslint:recommended 被加入到 extends 后,代码质量检查的时候就会使用这套规则:https://eslint.org/docs/latest/rules/

团队协作(一)—— 你不知道的 ESLint + Prettier_第15张图片

对于 Vue 项目来说,仅使用官方提供的规则显然是不够或者存在冲突。所以,你会发现还有诸如:plugin:vue/vue3-essential@vue/eslint-config-typescript/recommended@vue/eslint-config-prettier 等规则。它们在数组中的顺序是有讲究的,后一个配置将会继承它前面的配置,然后将这些共享配置扩充为一套符合项目要求的完整规则

  • plugin:vue/vue3-essential:Vue.js 的官方 ESLint (规则)插件。Official ESLint plugin for Vue.js. This plugin allows us to check the