前提
本文并不單獨講解 ESLint 和 Prettier 如何配置和運行。
問題
想在團隊中推行一定的代碼規范,并給不符合規范的代碼做檢測和提示。
方案
代碼規范校驗使用 ESLint,但是一開始 ESLint 只有檢測告訴你哪里有問題,常常出現的情況就是一堆 warning,改起來很痛苦。后來 ESLint 提供了 $ ESLint filename --fix
的命令可以自動幫你修復一些不符合規范的代碼。Prettier 是一個代碼格式化工具,可以幫你把代碼格式化成可讀性更好的格式,最典型的就是一行代碼過長的問題。
Lint和Prettier區別
那 ESLint 和 Prettier 的區別是什么呢?eslint(包括其他一些 lint 工具)的主要功能包含代碼格式的校驗,代碼質量的校驗。而 Prettier 只是代碼格式的校驗(并格式化代碼),不會對代碼質量進行校驗。代碼格式問題通常指的是:單行代碼長度、tab長度、空格、逗號表達式等問題。而代碼質量問題指的是:未使用變量、三等號、全局變量聲明等問題。
Lint和Prettier配合使用
為什么要兩者配合使用?因為,第一在 ESLint 推出 --fix 參數前,ESLint 并沒有自動化格式代碼的功能,要對一些格式問題做批量格式化只能用 Prettier 這樣的工具。第二 ESLint 的規則并不能完全包含 Prettier 的規則,兩者不是簡單的誰替代誰的問題。但是在 ESLint 推出 --fix 命令行參數之后,如果你覺得 ESLint 提供的格式化代碼夠用了,也可以不使用 Prettier。
ESLint 和 Prettier 相互合作的時候有一些問題,對于他們交集的部分規則,ESLint 和 Prettier 格式化后的代碼格式不一致。導致的問題是:當你用 Prettier 格式化代碼后再用 ESLint 去檢測,會出現一些因為格式化導致的 warning。這個時候有兩個解決方案:
- 運行 Prettier 之后,再使用 eslint --fix 格式化一把,這樣把沖突的部分以 ESLint 的格式為標準覆蓋掉,剩下的 warning 就都是代碼質量問題了。
- 在配置 ESLint 的校驗規則時候把和 Prettier 沖突的規則 disable 掉,然后再使用 Prettier 的規則作為校驗規則。那么使用 Prettier 格式化后,使用 ESLint 校驗就不會出現對前者的 warning。
為什么不能先使用 ESLint 再使用 Prettier。針對方案1,如果你后使用 Prettier,那么格式化后提交的代碼在下一次或者別人 checkout 代碼后是通不過 lint 校驗的。針對方案2,其實是可以的,但是本人在實踐中看社區方案的時候有提到某些情況下 eslint --fix 和 prettier 混用會出現格式問題。所以保險起見還是先用 perttier 格式化,再用 eslint 命令校驗,而不用 eslint --fix 命令去格式化。
方案一實踐
-
安裝 prettier-eslint(Tip:所有方案前提是你已經安裝 eslint 和 prettier 相關包):
$ npm install --save-dev prettier-eslint prettier-eslint-cli
-
運行
$ npm prettier-eslint "src/**/*.js"
prettier-eslint 會一次執行 prettier 和 eslint --fix 命令。整個流程是:Code ?? prettier ?? eslint --fix ?? Formatted Code。prettier-eslint 的各種參數請參看 https://github.com/prettier/prettier-eslint-cli 。
方案二實踐
方案二的思路主要是在 eslint 的規則配置文件上做文章,安裝特定的 plugin,把其配置到規則的尾部,實現 prettier 規則對 eslint 規則的覆蓋。
-
安裝 plugin:
$ npm install --save-dev eslint-config-prettier
-
在 .eslintrc.* 文件里面的 extends 字段添加:
{ "extends": [ ..., "已經配置的規則", + "prettier", + "prettier/@typescript-eslint" ] }
我使用的是 TypeScript,所以 plugin 的名字是 prettier/@typescript-eslint。
如果你想 disable 掉更多的規則可以是如下:
{ "extends": [ ..., "已經配置的規則", "prettier", "prettier/@typescript-eslint", "prettier/babel", "prettier/flowtype", "prettier/react", "prettier/standard", "prettier/unicorn", "prettier/vue" ] }
看名字應該能猜到每個對應的是 disable 哪些規則了吧。
-
完成上述兩步可以實現的是運行 eslint 命令會按照 prettier 的規則做相關校驗,但是還是需要分別運行 prettier 和 eslint 命令。社區有一個方案整合了上述兩步,在使用 eslint --fix 時候,實際使用 prettier 來替代 eslint 的格式化功能。
安裝:$ npm install --save-dev eslint-plugin-prettier
修改 .eslintrc.*
{ "extends": [ ..., "已經配置的規則", "plugin:prettier/recommended" ] }
這個時候你運行 eslint --fix 實際使用的是 Prettier 去格式化文件。eslint-plugin-prettier 具體詳細的配置見:https://github.com/prettier/eslint-plugin-prettier
和VSCode集成使用
上面所有講到的內容都是在你寫完代碼去校驗的,使用的是命令行工具。如果你是一個新手,對規范不是很熟悉,那么碰到的問題就是寫完代碼運行命令行工具后產生海量的 warning。這個時候改起來真的很打擊積極性。如果能夠在寫的代碼的時候讓編輯器提示就能及時改正了。自然我們社區也提供了很多方案,各家的編輯器 Atom、Sublime、VSCode 等主流的編輯器都有相關的插件。我就以 VSCode 為例:
-
下載 ESLint Extension
vscode-eslint-extension.png 配置 .eslintrc.*
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
},
// 其他配置
};
- 因為我使用了 TypeScript 所以 .eslintrc.* 配置項的值和 javascript 有點不一樣,但是原理是一樣的。如果你沒有使用 TypeScript 那么到上面步驟就結束了。但是 VSCode 的 ESLint 插件沒有天然支持 ts 文件,所以我們還必須自己建立一個設置文件。在項目根目錄下新建 .vscode/settings.json 文件,內容如下:
{ "eslint.validate": [ "javascript", "javascriptreact", { "language": "typescript", "autoFix": true }, { "language": "typescriptreact", "autoFix": true } ] }
你就可以看到編輯器對代碼有實時提示了。
強制校驗和格式化
講到這里兩個工具配合使用已經講好了,但是這些步驟都依賴于人工自覺,要做到一點點強制功能,我們就可以用到 husky lint-staged 來在 git commit 前強制代碼格式化和代碼校驗。
-
安裝
$npm install --save-dev husky lint-staged
-
修改 package.json:
{ "name": "project-name", ..., + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [ + "prettier --write", + "eslint", + "git add" + ] + }, }
那么在運行 git commit 時候,自動會先去運行 prettier --write 格式化代碼,再運行 eslint 校驗代碼是否符合規范。這兩步都通過后才會提交代碼。如果任何一步失敗,則會停止提交。
如果你使用方案一,lint-staged 配置的命令:
{
"name": "project-name",
...,
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
+ "prettier-eslint",
+ "git add"
+ ]
+ },
}
如果你使用了方案二中的 eslint-plugin-prettier,lint-staged 配置的命令:
{
"name": "project-name",
...,
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
+ "eslint --fix",
+ "git add"
+ ]
+ },
}
參考文獻
- https://eslint.org/
- https://prettier.io/
- https://restishistory.net/blog/whats-the-difference-between-eslint-and-prettier.html
如果使用的是 TypeScript: