前言

团队开发中,每个人的编码习惯不同,代码格式不同。这就会导致代码难看,难以维护。统一代码风格可以:

  1. 增强代码的可读性,降低维护成本。
  2. 有利于代码审查。
  3. 养成规范代码的习惯,有利于自身成长。

下面将使用 ESLint + Prettier + husky + lint-staged 对代码进行规范及检查。

ESLint 和 Prettier

区别

ESLint(包括其他一些 lint 工具)的主要功能包含代码格式和代码质量的校验,而 Prettier 只是代码格式的校验,不会对代码质量进行校验。代码格式问题通常指的是:单行代码长度、tab 长度、空格、逗号表达式等问题。代码质量问题指的是:未使用变量、三等号、全局变量声明等问题。

配合

为什么要两者配合使用?

  • 第一,ESLint 推出 –fix 参数前,ESLint 并没有自动格式化代码的功能,而 Prettier 可以自动格式化代码。

  • 第二,虽然 ESLint 也可以校验代码格式,但 Prettier 更擅长,同时 Prettier 也支持其他语言。

相关依赖

nodejs 版本为 16.19.0。

1
2
3
4
5
6
7
"eslint": "^8.57.0"
"eslint-config-prettier": "^9.1.0"
"eslint-plugin-prettier": "^4.2.1"
"husky": "^9.0.11"
"lint-staged": "^15.2.2"
"prettier": "^2.8.8"
"vue-eslint-parser": "^9.4.2"

如果 nodejs 版本为 14.x,husky 版本改为 8.0.3,lint-staged 版本改为 13.2.0。

一、ESlint

1、安装 eslint

1
npm install eslint --save-dev

2、配置 eslint

根目录创建 .eslintrc.js。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [],
parser: "vue-eslint-parser",
parserOptions: {
sourceType: "module",
},
plugins: [],
rules: {
// ... 这里配置相关校验规则
},
};

二、Prettier

1、安装 prettier

1
2
# nodejs 版本在16及以下时,请安装 2.x 版本 prettier
npm install prettier --save-dev

2、配置 prettier

根目录创建 .prettierrc.js文件,并定义想要的代码样式。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用 tab 缩进,而使用空格
useTabs: false,
// 行尾需要有分号
semi: true,
// 使用单引号代替双引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: "as-needed",
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 末尾使用逗号
trailingComma: "all",
// 大括号内的首尾需要空格 { foo: bar }
bracketSpacing: true,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: "always",
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: "preserve",
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: "css",
// 换行符使用 lf
endOfLine: "lf",
};

最好再加上 .prettierignore 文件,避免把不必要的文件也进行格式化。

1
2
3
4
5
6
7
#ignore
node_modules
.DS_Store
yarn*
*-lock*
dist*
public/

三、lint-staged

lint-staged 可以让你在 Git 暂存(staged)区域中的文件上运行脚本,通常用于在提交前对代码进行格式化、静态检查等操作。

1、安装 lint-staged

1
npm install lint-staged --save-dev

2、配置 lint-staged

在 package.json 文件中添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
//... 其他配置
"scripts": {
//... 其他配置
"lint-staged": "lint-staged"
},
"lint-staged": {
// *.{js,vue} 校验暂存区的文件类型
// 校验命令,执行 eslint 、prettier
// prettier --write、 eslint --fix 两个命令,在 git 提交时会对代码进行校验,并对不符合要求的代码自动进行格式化修复。
"*.{js,vue,ts,tsx}": ["prettier --write", "eslint --fix"]
}
}

四、Husky

husky 是一个 Git 钩子(Git hooks)工具,它可以让你在 Git 事件发生时执行脚本,进行代码格式化、测试等操作。

常见的钩子:

  • pre-commit:在执行 Git commit 命令之前触发,用于在提交代码前进行代码检查、格式化、测试等操作。
  • commit-msg:在提交消息(commit message)被创建后,但提交操作尚未完成之前触发,用于校验提交消息的格式和内容。
  • pre-push:在执行 Git push 命令之前触发,用于在推送代码前进行额外检查、测试等操作。

1、安装 Husky

项目根目录下执行:

1
npm install husky --save-dev

2、配置 Husky

1
npx husky init

在 package.json 文件中添加配置:

1
2
3
4
5
6
7
8
9
10
11
12
{
//... 其他配置
"scripts": {
//... 其他配置
"prepare": "husky init"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}

3、创建挂钩

在 Git 提交之前做 eslint 语法校验 。

创建钩子脚本文件。

1
npx husky add .husky/pre-commit "npm run lint-staged"

执行成功后,.husky 目录多出一个 pre-commit 文件,文件中的内容为 npm run lint-staged

五、保存时,进行代码格式化

.vscode/settings.json 文件(没有就自行创建)中添加一下配置:

1
2
3
4
5
6
7
8
9
10
{
// ... 其他配置
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.requireConfig": true,
"eslint.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}

六、解决 eslint 和 prettier 冲突

有时,ESLint 的规则和 Prettier 的规则可能存在冲突,导致代码格式化不一致。使用 eslint-config-prettier 可以关闭 ESLint 中与 Prettier 冲突的规则。

1
npm i  eslint-config-prettier eslint-plugin-prettier --save-dev

.eslintrc.jsextends 中加入配置:

1
2
3
{
extends: ['plugin:prettier/recommended'],
}

七、注意事项

1、运行项目时,报错 Parsing error: Unexpected token <

1
npm i vue-eslint-parser

.eslintrc.js 中添加以下配置:

1
2
3
4
5
6
7
module.exports = {
// 需要先 npm i vue-eslint-parser
parser: "vue-eslint-parser",
parserOptions: {
sourceType: "module",
},
};

2、运行项目时,报错 Delete ␍ eslintprettier/prettier

由于历史原因,windows 下和 linux 下的文本文件的换行符不一致。

  • Windows 在换行的时候,同时使用了回车符 CR(carriage-return character)和换行符 LF(linefeed character)
  • 而 Mac 和 Linux 系统,仅仅使用了换行符 LF
  • 老版本的 Mac 系统使用的是回车符 CR

解决方案

  1. 使用 vue-cli-service lint 进行修复:
1
npm run lint --fix
  1. windows 电脑设置 git
1
git config --global core.autocrlf false

注意:git 全局配置之后,你需要重新拉取代码。

3、如果提交时报命令相关错误,大概率是依赖版本问题,具体修改可参考【注意事项】【4】。

4、项目运行时,报错 Module parse failed: Unexpected token

可能是 prettier 版本问题,编写此文章时,nodejs 版本为 16.19.0,使用的依赖版本如下:

1
2
3
4
5
6
7
"eslint": "^8.57.0"
"eslint-config-prettier": "^9.1.0"
"eslint-plugin-prettier": "^4.2.1"
"husky": "^9.0.11"
"lint-staged": "^15.2.2"
"prettier": "^2.8.8"
"vue-eslint-parser": "^9.4.2"

注:

  1. 如提交时报错,请检查版本是否过高。
  2. 上面 husky 9.0.11 的版本 nodejs 版本应大于等于 16。
  3. lint-staged 15+的版本,nodejs 版本应大于等于 18.12.0,实测 16.19.0 版本也可使用。
  4. 如果 nodejs 不能升级到 16 版本,则需降低 husky 和 lint-staged 版本。实测 nodejs 版本为 14.21.2 时,husky@8.0.3lint-staged@13.2.0 可正常使用,husky@8.0.3 的用法和该文档用法有些区别,具体请查看官方文档

5、运行时,报错Syntax Error: TypeError: eslint.CLIEngine is not a constructor

  1. 检查 package.json 中是否存在 @vue/cli-plugin-eslint,如存在,则去掉重新运行。
  2. 如果不是【1】的原因,尝试降级 eslint。

6、执行 npx husky add .husky/pre-commit "npm run lint-staged" 失败,显示以下信息。

1
2
3
4
Usage:
husky install [dir] (default: .husky)
husky uninstall
husky set|add <file> [cmd]
  1. 将命令拆分,先执行 npx husky add .husky/pre-commit;
  2. 打开生成的 .husky/pre-commit 文件,将文件中 undefined 代码改为 npm run lint-staged

八、项目中 .eslintrc.js 及 .prettier.js 的配置

.eslintrc.js 中的 rules

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
// ... 其他配置
rules: {
indent: ["error", 2], // 用于指定代码缩进的方式,这里配置为使用四个空格进行缩进。
"linebreak-style": [0, "error", "windows"], // 用于指定换行符的风格,这里配置为使用 Windows 风格的换行符(\r\n),这里前面配置 0,则代表关闭此校验,防止报错。
quotes: ["error", "single"], // 用于指定字符串的引号风格,这里配置为使用单引号作为字符串的引号。
semi: ["error", "always"], //用于指定是否需要在语句末尾添加分号,这里配置为必须始终添加分号。
"@typescript-eslint/no-explicit-any": ["off"], // 用于配置 TypeScript 中的 "any" 类型的使用规则,这里配置为关闭禁止显式使用 "any" 类型的检查。
"no-unused-vars": "off",
},
};

.prettier.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用 tab 缩进,而使用空格
useTabs: false,
// 行尾需要有分号
semi: true,
// 使用单引号代替双引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: "as-needed",
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 末尾使用逗号
trailingComma: "all",
// 大括号内的首尾需要空格 { foo: bar }
bracketSpacing: true,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: "always",
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: "preserve",
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: "css",
// 换行符使用 lf
endOfLine: "lf",
};