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

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

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。