SSShooter

SSShooter

Write like you're running out of time.

前端代码质量与团队协作终极指南

注意事项#

本文默认开发环境是 VSCode,团队内尽可能统一开发环境,避免编辑器差异、插件不兼容的问题。

以下提到的工具核心都是配置文件,并且这些配置文件通常都能通过多种格式配置,例如 .js.yaml.json 或者直接在 package.json 中配置。后面不再赘述相关设定,具体配置形式可以在官方文档查询,本文仅解析部分配置项目。

代码风格#

代码风格一致是项目协作的基石,使用 ESLint 和 Prettier 可以避免由于代码格式不一致带来的代码合并冲突,也可以提高代码可读性和可维护性。虽然在认识 ESLint 和 Prettier提到过,但是下面想要作为升级版讲得更完整一点。

ESlint#

本文介绍的第一个工具是 ESlint,其功能是:

  1. 提供代码规范,例如:建议你使用 === 而不是 ==,建议你未修改过的变量使用 const 而不是 let 等这样的规则
  2. 其次是格式化代码,控制缩进换行之类的问题

安装#

npm install --save-dev eslint

VSCode 插件#

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-plusplusno-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 插件#

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-commitcommit-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-commitcommit-msg 分别要使用的 lint-stagedcommitlint

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.jsonlint-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?》,讲得全面且清楚,下面贴一张来自此文的性能对比图:

npm,yarn,pnpm performance

可以按照喜好和兼容性自行选择,但是无论选择哪个管理器,非必要时,不要删除 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 版本管理器,这里推荐三款:

基本上可以做到一行命令安装版本、一行命令切换版本,十分方便。

.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 固定仓库等配置
  • 选择一种分支策略,使代码历史更有序

各位看完觉得有遗漏或者有什么不懂我再补充!

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.