<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從 ESLint 開始,說透我如何在團(tuán)隊(duì)項(xiàng)目中基于 Vue 做代碼校驗(yàn)

          共 28031字,需瀏覽 57分鐘

           ·

          2021-06-22 01:24

          這是布蘭的第 23 篇原創(chuàng)

          最近遇到了一個(gè)老項(xiàng)目,比較有意思的是這個(gè)項(xiàng)目集前后端的代碼于一起,而后端也會(huì)去修改前端代碼,所以就出現(xiàn)了后端用 IntelliJ IDEA 來開發(fā)前端項(xiàng)目,而前端用 VSCode 來開發(fā)前端項(xiàng)目的情況。于是乎,出現(xiàn)了代碼規(guī)范的問題,所以就有了這篇文章,整理了一下前端代碼校驗(yàn)以及在 Vue 項(xiàng)目中的實(shí)踐。

          閱讀完這篇文章,你可以收獲:

          • 能夠自己親手寫出一套 ESLint 配置;
          • 會(huì)知道業(yè)界都有哪些著名的 JS 代碼規(guī)范,熟讀它們可以讓你寫出更規(guī)范的代碼;
          • vue-cli 在初始化一個(gè)包含代碼校驗(yàn)的項(xiàng)目時(shí)都做了什么;
          • Prettier 是什么?為什么要使用它?如何與 ESLint 配合使用?
          • EditorConfig 又是什么?如何使用?
          • 如何在 VSCode 中通過插件來協(xié)助代碼校驗(yàn)工作;
          • 如何保證 push 到遠(yuǎn)程倉庫的代碼是符合規(guī)范的;

          下面開始閱讀吧,如果你對 ESLint 比較熟悉,可以直接跳過這個(gè)部分。

          ESLint 是什么

          ESLint 是一個(gè)集代碼審查和修復(fù)的工具,它的核心功能是通過配置一個(gè)個(gè)規(guī)則來限制代碼的合法性和風(fēng)格。

          配置解析器和解析參數(shù)

          ESLint 的解析器,早期的時(shí)候用的是 Esprima[1],后面基于 Esprima v1.2.2 版本開發(fā)了一個(gè)新的解析器 Espree[2],并且把它當(dāng)做默認(rèn)解析器。

          除了使用 ESLint 自帶的解析器外,還可以指定其他解析器:

          • @babel/eslint-parser[3]:使 Babel 和 ESLint 兼容,對一些 Babel 語法提供支持;
          • @typescript-eslint/parser[4]:TSLint 被棄用后,TypeScript 提供了此解析器用于將其與 ESTree 兼容,使 ESLint 對 TypeScript 進(jìn)行支持;

          為項(xiàng)目指定某個(gè)選擇器的原則是什么?

          • 如果你的項(xiàng)目用到了比較新的 ES 語法,比如 ES2021 的 Promise.any(),那就可以指定 @babel/eslint-parser 為解析器;
          • 如果項(xiàng)目是基于 TS 開發(fā)的,那就使用 @typescript-eslint/parser;

          如果你對 ES 最新標(biāo)準(zhǔn)還不熟悉,可以看看這篇文章:送你一份精心總結(jié)的3萬字ES6實(shí)用指南(下)

          除了指定解析器 parser 外,還可以額外配置解析器參數(shù) parserOption:

          {
              // ESLint 默認(rèn)解析器,也可以指定成別的
              parser"espree"
              parserOption: {
                  // 指定要使用的 ECMAScript 版本,默認(rèn)值 5
                  ecmaVersion5,
                  // 設(shè)置為 script (默認(rèn)) 或 module(如果你的代碼是 ECMAScript 模塊)
                  sourceType"script",
                  // 這是個(gè)對象,表示你想使用的額外的語言特性,所有選項(xiàng)默認(rèn)都是 false
                  ecmafeatures: {
                      // 是否允許在全局作用域下使用 return 語句
                      globalReturnfalse,
                      // 是否啟用全局 strict 模式(嚴(yán)格模式)
                      impliedStrictfalse,
                      // 是否啟用JSX
                      jsxfalse,
                      // 是否啟用對實(shí)驗(yàn)性的objectRest/spreadProperties的支持
                      experimentalObjectRestSpreadfalse
                  }
              }
          }

          指定環(huán)境 env

          指定不同的環(huán)境可以給對應(yīng)環(huán)境下提供預(yù)設(shè)的全局變量。比如說在 browser 環(huán)境下,可以使用 window 全局變量;在 node 環(huán)境下,可以使用 process 全局變量等;

          ESLint 中可配置的環(huán)境比較多,這里有份完整的環(huán)境列表[5],下面列出幾個(gè)比較常見的:

          • browser:瀏覽器全局變量;
          • node:Node.js 全局變量和作用域;
          • es6:es6 中除了模塊之外的其他特性,同時(shí)將自動(dòng)設(shè)置 parserOptions.ecmaVersion 參數(shù)為 6;以此類推 ES2017 是 7,而 ES2021 是 12;
          • es2017:parserOptions.ecmaVersion 為 8;
          • es2020:parserOptions.ecmaVersion 為 11;
          • es2021:parserOptions.ecmaVersion 為 12;

          配置方式如下:

          {
              env: {
                  browsertrue,
                  nodetrue,
                  es6true,
                  commonjstrue,
                  mochatrue,
                  jquerytrue,
              }
          }

          可以指定多個(gè)環(huán)境并不意味著配置的環(huán)境越多越好,實(shí)際配置的時(shí)候還是得依據(jù)當(dāng)前項(xiàng)目的環(huán)境來選擇。

          配置全局變量 globals

          ESLint 的一些核心規(guī)則依賴于對代碼在運(yùn)行時(shí)可用的全局變量的了解。由于這些在不同環(huán)境之間可能會(huì)有很大差異,并且在運(yùn)行時(shí)會(huì)進(jìn)行修改,因此 ESLint 不會(huì)假設(shè)你的執(zhí)行環(huán)境中存在哪些全局變量。

          如果你想使用這些全局變量,那就可以通過 globals 來指定。比如在 react .eslintrc.js[6] 里就把 spyOnDev、 spyOnProd 等變量掛在了 global 下作為全局變量:

          {
              globals: {
                  spyOnDevtrue,
                  spyOnProdtrue,
              }
          }

          對于它的值需要特別說明下:

          • false、readable、readonly 這 3 個(gè)是等價(jià)的,表示變量只可讀不可寫;
          • true、writeable、writable 這 3 個(gè)是等價(jià)的,表示變量可讀可寫;

          配置擴(kuò)展 extends

          實(shí)際項(xiàng)目中配置規(guī)則的時(shí)候,不可能團(tuán)隊(duì)一條一條的去商議配置,太費(fèi)精力了。通常的做法是使用業(yè)內(nèi)大家普通使用的、遵循的編碼規(guī)范;然后通過 extends 去引入這些規(guī)范。extends 配置的時(shí)候接受字符串或者數(shù)組:

          {
              extends: [
                  'eslint:recommended',
                  'plugin:vue/essential',
                  'eslint-config-standard'// 可以縮寫成 'standard'
                  '@vue/prettier',
                  './node_modules/coding-standard/.eslintrc-es6'
              ]
          }

          從上面的配置,可以知道 extends 支持的配置類型可以是以下幾種

          • eslint 開頭的:是 ESLint 官方的擴(kuò)展;
          • plugin 開頭的:是插件類型擴(kuò)展,比如 plugin:vue/essential;
          • eslint-config 開頭的:來自 npm 包,使用時(shí)可以省略前綴 eslint-config-,比如上面的可以直接寫成 standard;
          • @開頭的:擴(kuò)展和 eslint-config 一樣,只是在 npm 包上面加了一層作用域 scope;
          • 一個(gè)執(zhí)行配置文件的相對路徑或絕對路徑;

          那有哪些常用的、比較著名擴(kuò)展可以被 extends 引入呢

          • eslint:recommended:ESLint 內(nèi)置的推薦規(guī)則,即 ESLint Rules 列表中打了鉤的那些規(guī)則;
          • eslint:all:ESLint 內(nèi)置的所有規(guī)則;
          • eslint-config-standard[7]:standard 的 JS 規(guī)范;
          • eslint-config-prettier[8]:關(guān)閉和 ESLint 中以及其他擴(kuò)展中有沖突的規(guī)則;
          • eslint-config-airbnb-base:airbab 的 JS 規(guī)范;
          • eslint-config-alloy[9]:騰訊 AlloyTeam 前端團(tuán)隊(duì)出品,可以很好的針對你項(xiàng)目的技術(shù)棧進(jìn)行配置選擇,比如可以選 React、Vue(現(xiàn)已支持 Vue 3.0)、TypeScript 等;

          使用插件 plugins

          ESLint 提供插件是干嘛用的

          ESLint 雖然可以定義很多的 rules,以及通過 extends 來引入更多的規(guī)則,但是說到底只是檢查 JS 語法。如果需要檢查 Vue 中的 template 或者 React 中的 jsx,就束手無策了。所以引入插件的目的就是為了增強(qiáng) ESLint 的檢查能力和范圍。

          如何配置插件

          ESLint 相關(guān)的插件的命名形式有 2 種:不帶命名空間的和帶命名空間的,比如:

          • eslint-plugin- 開頭的可以省略這部分前綴;
          • @/ 開頭的;
          {
              plugins: [
                  'jquery',          // 是指 eslint-plugin-jquery
                  '@jquery/jquery',  // 是指 @jquery/eslint-plugin-jquery
                  '@foobar',         // 是指 @foobar/eslint-plugin
              ]
          }

          當(dāng)需要基于插件進(jìn)行 extends 和 rules 的配置的時(shí)候,需要加上插件的引用,比如:

          {
              plugins: [
                  'jquery',   // eslint-plugin-jquery
                  '@foo/foo'// @foo/eslint-plugin-foo
                  '@bar,      // @bar/eslint-plugin
              ],
              extends: [
                  '
          plugin:jquery/recommended',
                  '
          plugin:@foo/foo/recommended',
                  '
          plugin:@bar/recommended'
              ],
              rules: {
                  '
          jquery/a-rule': 'error',
                  '
          @foo/foo/some-rule': 'error',
                  '
          @bar/another-rule': 'error'
              },
          }

          以上配置來自 ESLint plugins[10]

          配置規(guī)則 rules

          ESLint 提供了大量內(nèi)置的規(guī)則,這里是它的規(guī)則列表 ESLint Rules,除此之外你還可以通過插件來添加更多的規(guī)則。

          規(guī)則的校驗(yàn)說明,有 3 個(gè)報(bào)錯(cuò)等級

          • off 或 0:關(guān)閉對該規(guī)則的校驗(yàn);
          • warn 或 1:啟用規(guī)則,不滿足時(shí)拋出警告,且不會(huì)退出編譯進(jìn)程;
          • error 或 2:啟用規(guī)則,不滿足時(shí)拋出錯(cuò)誤,且會(huì)退出編譯進(jìn)程;

          通常規(guī)則只需要配置開啟還是關(guān)閉即可;但是也有些規(guī)則可以傳入屬性,比如:

          {
              rules: {
                  'quotes': ['error''single'],  // 如果不是單引號,則報(bào)錯(cuò)
                  'one-var': ['error', {
                      'var''always',  // 每個(gè)函數(shù)作用域中,只允許 1 個(gè) var 聲明
                      'let''never',   // 每個(gè)塊作用域中,允許多個(gè) let 聲明
                      'const''never'// 每個(gè)塊作用域中,允許多個(gè) const 聲明
                  }]
              }
          }

          如何知道某個(gè)擴(kuò)展有哪些規(guī)則可以配置,以及每個(gè)規(guī)則具體限制?這里直接給出業(yè)內(nèi)著名且使用比較多的規(guī)則列表的快速鏈接:

          • ESLint rules,這整個(gè)列表對應(yīng) eslint:all,而打鉤 ?? 的是 eslint:recommenmed;
          • Prettier rules
          • standard rules
          • airbnb rules
          • AlloyTeam vue rules

          規(guī)則的優(yōu)先級

          • 如果 extends 配置的是一個(gè)數(shù)組,那么最終會(huì)將所有規(guī)則項(xiàng)進(jìn)行合并,出現(xiàn)沖突的時(shí)候,后面的會(huì)覆蓋前面的;
          • 通過 rules 單獨(dú)配置的規(guī)則優(yōu)先級比 extends 高;

          其他配置

          配置當(dāng)前目錄為 root

          ESLint 檢測配置文件步驟:


            1. 在要檢測的文件同一目錄里尋找 .eslintrc.* 和 package.json;

            1. 緊接著在父級目錄里尋找,一直到文件系統(tǒng)的根目錄;

            1. 如果在前兩步發(fā)現(xiàn)有 root:true 的配置,停止在父級目錄中尋找 .eslintrc;

            1. 如果以上步驟都沒有找到,則回退到用戶主目錄 ~/.eslintrc 中自定義的默認(rèn)配置;

          通常我們都習(xí)慣把 ESLint 配置文件放到項(xiàng)目根目錄,因此可以為了避免 ESLint 校驗(yàn)的時(shí)候往父級目錄查找配置文件,所以需要在配置文件中加上 root: true。

          {
              roottrue,
          }

          添加共享數(shù)據(jù)

          ESLint 支持在配置文件添加共享設(shè)置,你可以添加 settings 對象到配置文件,它將提供給每一個(gè)將被執(zhí)行的規(guī)則。如果你想添加的自定義規(guī)則而且使它們可以訪問到相同的信息,這將會(huì)很有用,并且很容易配置:

          {
              settings: {
                  sharedData'Hello'
              }, 
          }

          參考:ESLint配置文件.eslintrc參數(shù)說明[11]

          針對個(gè)別文件設(shè)置新的檢查規(guī)則

          比如 webpack 的中包含了某些運(yùn)行時(shí)的 JS 文件,而這些文件是只跑在瀏覽器端的,所以需要針對這部分文件進(jìn)行差異化配置:

          overrides: [
              {
                  files: ["lib/**/*.runtime.js""hot/*.js"],
                  env: {
                      es6false,
                      browsertrue
                  },
                      globals: {
                      Promisefalse
                  },
                  parserOptions: {
                      ecmaVersion5
                  }
              }
          ]

          以上配置來自 webpack .eslintrc.js[12]

          如何校驗(yàn)

          上面細(xì)說了 ESLint 的各種配置項(xiàng),以及針對 Vue 項(xiàng)目如何進(jìn)行差異配置的說明。

          現(xiàn)在我們知道了如何配置,但是你知道這些配置都是配置到哪里的嗎?

          配置方式

          ESLint 支持 3 種配置方式:

          • 命令行:不推薦,不做介紹;
          • 單文件內(nèi)注釋:不推薦,不做介紹;
          • 配置文件:配置文件的類型可以是好幾種,比如:.js、.yml、json 等。推薦使用 .eslintrc.js;

          下面通過命令來生成一個(gè)配置文件:

          # 安裝 eslint
          npm i eslint -D

          # 初始化一個(gè)配置文件
          npx eslint --init

          最后會(huì)在當(dāng)前目錄生成一個(gè) .eslintrc.js 文件。這里就不把代碼貼出來了,沒參考意義。

          上面我們知道了可以將配置統(tǒng)一寫到一個(gè)配置文件里,但是你知道該如何去觸發(fā)這個(gè)配置文件的校驗(yàn)規(guī)則嘛?

          校驗(yàn)單個(gè)文件

          // 校驗(yàn) a.js 和 b.js
          npx eslint a.js b.js

          // 校驗(yàn) src 和 scripts 目錄
          npx eslint src scripts

          校驗(yàn)別的類型的文件

          通常 ESLint 只能校驗(yàn) JS 文件。比如需要校驗(yàn) .vue 文件,光配置 vue 插件和 vue-eslint-parser 解析器是不夠的,還需要讓 ESLint 在查找文件的時(shí)候找到 .vue 文件。

          可以通過 --ext 來指定具體需要校驗(yàn)的文件:

          npx eslint --ext .js,.jsx,.vue src

          自動(dòng)修復(fù)部分校驗(yàn)錯(cuò)誤的代碼

          rules 列表項(xiàng)中標(biāo)識了一個(gè)扳手 ?? 圖案的規(guī)則就標(biāo)識該規(guī)則是可以通過 ESLint 工具自動(dòng)修復(fù)代碼的。如何自動(dòng)修復(fù)呢?通過 --fix 即可。比如對于 ESLint Rules 里的這個(gè) semi 規(guī)則,它就是帶扳手圖案的。

          對于如下的 a.js 代碼:

          const num = 12

          當(dāng)在配置文件配置了 'semi': [2, 'always'] 后,運(yùn)行命令:

          npx eslint --fix a.js

          校驗(yàn)直接就通過了,且會(huì)自動(dòng)修復(fù)代碼,在代碼末尾自動(dòng)加上分號。

          把校驗(yàn)命令加到 package.json

          檢驗(yàn)命令比較長,也難記,習(xí)慣上會(huì)把這些命名直接寫到 package.json 里:

          {
              "scripts": {
                  "lint""npx eslint --ext .js,.jsx,.vue src",
                  "lint:fix""npx eslint --fix --ext .js,.jsx,.vue src",
              }
          }

          過濾一些不需要校驗(yàn)的文件

          對于一些公共的 JS、測試腳本或者是特定目錄下的文件習(xí)慣上是不需要校驗(yàn)的,因此可以在項(xiàng)目根目錄通過創(chuàng)建一個(gè) .eslintignore 文件來配置,告訴 ESLint 校驗(yàn)的時(shí)候忽略它們:

          public/
          src/main.js

          除了 .eslintignore 中指定的文件或目錄,ESLint 總是忽略 /node_modules/ 和 /bower_components/ 中的文件;因此對于一些目前解決不了的規(guī)則報(bào)錯(cuò),但是如果又急于打包上線,在不影響運(yùn)行的情況下,我們就可以利用 .eslintignore 文件將其暫時(shí)忽略。

          在 Vue 項(xiàng)目中的實(shí)踐

          上面把 ESLint 的幾乎所有的配置參數(shù)和校驗(yàn)方式都詳細(xì)的介紹了一遍,但是如果想在項(xiàng)目中落地,僅僅靠上面的知識還是不夠的。下面將細(xì)說如何在 Vue 中落地代碼校驗(yàn)。

          關(guān)于如何在 Vue 中落地代碼校驗(yàn),一般是有 2 種情況:

          • 通過 vue-cli 初始化項(xiàng)目的時(shí)候已經(jīng)選擇了對應(yīng)的校驗(yàn)配置
          • 對于一個(gè)空的 Vue 項(xiàng)目,想接入代碼校驗(yàn)

          其實(shí)這 2 種情況最終的校驗(yàn)的核心配置都是一樣的,只是剛開始的時(shí)候安裝的包有所區(qū)別。下面通過分析 vue-cli 配置的代碼校驗(yàn),來看看它到底做了哪些事情,通過它安裝的包以及包的作用,我們就會(huì)知道如何在空項(xiàng)目中配置代碼校驗(yàn)了。

          通過 vue-cli 初始化的項(xiàng)目

          如果你的項(xiàng)目最初是通過 vue-cli 新建的,那么在新建的時(shí)候會(huì)讓你選

          • 是否支持 eslint;
          • 是否開啟保存校驗(yàn);
          • 是否開啟提交前校驗(yàn);

          如果都開啟了話,會(huì)安裝如下幾個(gè)包:

          • eslint:前面 2 大章節(jié)介紹的就是這玩意,ESLint 出品,是代碼校驗(yàn)的基礎(chǔ)包,且提供了很多內(nèi)置的 Rules,比如 eslint:recommended 經(jīng)常被作為項(xiàng)目的 JS 檢查規(guī)范被引入;
          • babel-eslint:一個(gè)對 Babel 解析器的包裝,使其能夠與 ESLint 兼容;
          • lint-staged:請看后面 pre-commit 部分;
          • @vue/cli-plugin-eslint
          • eslint-plugin-vue

          下面重點(diǎn)介紹 @vue/cli-plugin-eslint 和 eslint-plugin-vue,說下這 2 個(gè)包是干嘛的。

          @vue/cli-plugin-eslint

          這個(gè)包它主要干了 2 件事情:

          第一件事

          往 package.json 里注冊了一個(gè)命令:

          {
              "scripts": {
                  "lint""vue-cli-service lint"
              }
          }

          執(zhí)行這個(gè)命令之后,它會(huì)去檢查和修復(fù)部分可以修復(fù)的問題。默認(rèn)查找的文件是 src 和 tests 目錄下所有的 .js,.jsx,.vue 文件,以及項(xiàng)目根目錄下所有的 js 文件(比如,也會(huì)檢查 .eslintrc.js)。

          當(dāng)然你也可以自定義的傳入?yún)?shù)和校驗(yàn)文件:

          vue-cli-service lint [options] [...files]

          支持的參數(shù)如下:

          • --no-fix: 不會(huì)修復(fù) errors 和 warnings;
          • --max-errors [limit]:指定導(dǎo)致出現(xiàn) npm ERR 錯(cuò)誤的最大 errors 數(shù)量;

          第二件事

          增加了代碼保存觸發(fā)校驗(yàn)的功能 lintOnSave,這個(gè)功能默認(rèn)是開啟的。如果想要關(guān)閉這個(gè)功能,可以在 vue.config.js 里配置,習(xí)慣上只開啟 development 環(huán)境下的代碼保存校驗(yàn)功能:

          module.exports = {
              lintOnSave: process.env.NODE_ENV === 'development',
          }

          lintOnSave 參數(shù)說明:

          • true 或者 warning:開啟保存校驗(yàn),會(huì)將 errors 級別的錯(cuò)誤在終端中以 WARNING 的形式顯示。默認(rèn)的,WARNING 將不會(huì)導(dǎo)致編譯失??;
          • false:不開啟保存校驗(yàn);
          • error:開啟保存校驗(yàn),會(huì)將 errors 級別的錯(cuò)誤在終端中以 ERROR 的形式出現(xiàn),會(huì)導(dǎo)致編譯失敗,同時(shí)瀏覽器頁面變黑,顯示 Failed to compile。

          eslint-plugin-vue

          eslint-plugin-vue 是對 .vue 文件進(jìn)行代碼校驗(yàn)的插件。

          針對這個(gè)插件,它提供了這幾個(gè)擴(kuò)展

          • plugin:vue/base:基礎(chǔ)
          • plugin:vue/essential:預(yù)防錯(cuò)誤的(用于 Vue 2.x)
          • plugin:vue/recommended:推薦的,最小化任意選擇和認(rèn)知開銷(用于 Vue 2.x);
          • plugin:vue/strongly-recommended:強(qiáng)烈推薦,提高可讀性(用于 Vue 2.x);
          • plugin:vue/vue3-essential:(用于 Vue 3.x)
          • plugin:vue/vue3-strongly-recommended:(用于 Vue 3.x)
          • plugin:vue/vue3-recommended:(用于 Vue 3.x)

          各擴(kuò)展規(guī)則列表:vue rules

          看到這么一堆的擴(kuò)展,是不是都不知道選哪個(gè)了

          代碼規(guī)范的東西,原則還是得由各自的團(tuán)隊(duì)去磨合商議出一套適合大家的規(guī)則。不過,如果你用的是 Vue2,我這里可以推薦 2 套 extends 配置:

          {
              // Vue 官方示例上的配置
             extends: ['eslint:recommended''plugin:vue/recommended'],  
             
             // 或者使用 AlloyTeam 團(tuán)隊(duì)那套
             extends: ['alloy''alloy/vue']
          }

          配置和插件對應(yīng)的解析器

          如果是 Vue 2.x 項(xiàng)目,配置了 eslint-plugin-vue 插件和 extends 后,template 校驗(yàn)還是會(huì)失效,因?yàn)椴还苁?ESLint 默認(rèn)的解析器 Espree 還是 babel-eslint 都只能解析 JS,無法解析 template 的內(nèi)容。

          而 vue-eslint-parser 只能解析 template 的內(nèi)容,但是不會(huì)解析 JS,因此還需要對解析器做如下配置:

          {
              parser'vue-eslint-parser',
              parseOptions: {
                  parser'babel-eslint',
                  ecmaVersion12,
                  sourceType'module'
              },
              extends: [
                  'eslint:recommended'
                  'plugin:vue/recommended'
              ],
              plugins: ['vue']
          }

          參考:eslint-plugin-vue faq[13]

          讓 Prettier 管控代碼風(fēng)格

          針對 Prettier 不得不提出以下疑問?

          • Prettier 是什么?
          • 為什么有了 ESLint,還需要引入 Prettier 呢?它兩之間有什么區(qū)別?
          • 如何配置 Prettier?
          • Prettier 如何和 ESLint 結(jié)合使用?

          Prettier 是什么

          用它自己的話來說:我是一個(gè)自以為是的代碼格式化工具,而且我支持的文件類型很多,比如:

          • JavaScript(包括實(shí)驗(yàn)中的特性)
          • JSX
          • Vue
          • TypeScript
          • CSS、Less、SCSS
          • HTML
          • JSON
          • Markdown

          以及還有一些其他類型的文件。

          Prettier 對比 ESLint

          我們知道 ESLint 負(fù)責(zé)了對代碼的校驗(yàn)功能,并且主要提供了 2 類規(guī)則:

          • 檢查格式化的規(guī)則
          • 檢查代碼質(zhì)量的規(guī)則

          說到底 ESLint 就是通過一條條的規(guī)則去限制代碼的規(guī)范,但是這些規(guī)則畢竟是有限的,而且更重要的是這些規(guī)則的重點(diǎn)并不在代碼風(fēng)格上,所以單憑 ESLint 并不能完全的統(tǒng)一代碼風(fēng)格。

          這個(gè)時(shí)候就需要引入 Prettier 了,因?yàn)樗傻氖戮褪侵还艽a格式化,不管代碼質(zhì)量。

          Prettier:在代碼風(fēng)格這一塊,我一直拿捏的死死的。

          如何配置 Prettier

          初始化操作:

          # 安裝包
          npm i prettier -D  

          # 新建 .prettierrc.js
          echo module.exports = {} > .prettierrc.js

          # 新建 .prettierignore
          echo > .prettierignore

          Prettier 支持可以配置參數(shù)不多,總共才 21 個(gè),這里是所有參數(shù)的說明 prettier options[14]

          所有參數(shù)都有默認(rèn)值,也就是說即使你沒有配置 .prettierrc.js,當(dāng)你用 Prettier 去格式化代碼的時(shí)候全部都會(huì)走默認(rèn)配置。針對個(gè)別參數(shù),你不想用默認(rèn)設(shè)置的話,就可以在 .prettierrc.js 配置具體想要的值。

          如下,把項(xiàng)目中會(huì)用到的參數(shù)進(jìn)行一個(gè)說明:

          module.exports = {
              printWidth80,                    //(默認(rèn)值)單行代碼超出 80 個(gè)字符自動(dòng)換行
              tabWidth2,                       //(默認(rèn)值)一個(gè) tab 鍵縮進(jìn)相當(dāng)于 2 個(gè)空格
              useTabstrue,                     // 行縮進(jìn)使用 tab 鍵代替空格
              semifalse,                       //(默認(rèn)值)語句的末尾加上分號
              singleQuotetrue,                 // 使用單引號
              quoteProps'as-needed',           //(默認(rèn)值)僅僅當(dāng)必須的時(shí)候才會(huì)加上雙引號
              jsxSingleQuotetrue,              // 在 JSX 中使用單引號
              trailingComma'all',              // 不用在多行的逗號分隔的句法結(jié)構(gòu)的最后一行的末尾加上逗號
              bracketSpacingtrue,              //(默認(rèn)值)在括號和對象的文字之間加上一個(gè)空格
              jsxBracketSameLinetrue,          // 把 > 符號放在多行的 JSX 元素的最后一行
              arrowParens'avoid',              // 當(dāng)箭頭函數(shù)中只有一個(gè)參數(shù)的時(shí)候可以忽略括弧
              vueIndentScriptAndStylefalse,    //(默認(rèn)值)對于 .vue 文件,不縮進(jìn) <script> 和 <style> 里的內(nèi)容
              embeddedLanguageFormatting'off'// 不允許格式化內(nèi)嵌的代碼塊,比如 markdown  文件里的代碼塊
          };

          擴(kuò)展閱讀:關(guān)于 Trailing commas[15] 你或許想了解更多。

          然后可以通過命令來格式化代碼:

          # 將格式化當(dāng)前目錄及子目錄下所有文件
          npx prettier --write .

          # 檢查某個(gè)文件是否已經(jīng)格式化
          npx prettier --check src/main.js

          如果有些文件不想被 Prettier 格式化,可以將其寫入到 .prettierignore 里:

          build/
          package.json
          public/
          test/*.*

          Prettier 和 ESLint 一起干活更配哦

          上面介紹了 Prettier 的具體配置,這里主要介紹和 ESLint 結(jié)合使用的配置和注意事項(xiàng)。

          和 ESLint 配合使用需要用到 eslint-plugin-prettier 這個(gè)插件:

          npm i eslint-plugin-prettier -D

          配置:

          {
              plugins: ['prettier'],
              rules: {
                  'prettier/prettier''error'
              }
          }

          這個(gè)插件的工作原理是先調(diào)用 Prettier 對你的代碼進(jìn)行格式化,然后會(huì)把格式化前后不一致的地方進(jìn)行標(biāo)記,通過配置 'prettier/prettier': 'error' 此條規(guī)則會(huì)將標(biāo)記地方進(jìn)行 error 級別的報(bào)錯(cuò)提示,然后可以通過 ESLint 的 --fix 自動(dòng)修復(fù)功能將其修復(fù)。

          沖突了怎么辦

          通過前面的介紹,我們知道 ESLint 也是會(huì)對代碼風(fēng)格做一些限制的,而 Prettier 主要就是規(guī)范代碼風(fēng)格,所以在把它們結(jié)合一起使用的時(shí)候是存會(huì)在一些問題的。對于個(gè)別規(guī)則,會(huì)使得雙方在校驗(yàn)后出現(xiàn)代碼格式不一致的問題。

          那么當(dāng) Prettier 和 ESLint 出現(xiàn)沖突之后,該怎么辦呢?

          用 Prettier 的話來說很簡單,只要使用 eslint-config-prettier 就可以了。解決沖突的思路就是通過將這個(gè)包提供的擴(kuò)展放到 extends 最后面引入,依據(jù) rules 生效的優(yōu)先級,所以它會(huì)覆蓋前面起沖突的規(guī)則,比如:

          {
              extends: [
                  'eslint:recommended',
                  'prettier',  // 必須放最后
              ],
          }

          除了能覆蓋和 ESLint 中起沖突的規(guī)則之外,eslint-config-prettier 還能覆蓋來自以下插件的規(guī)則(只列了部分):

          • eslint-plugin-standard
          • eslint-plugin-vue

          那 eslint-config-prettier 到底提供了哪些覆蓋規(guī)則呢?直接看這個(gè)列表:eslint-config-prettier rules

          如果想覆蓋某些插件的規(guī)則,需要引入對應(yīng)插件的擴(kuò)展,比如:

          {
              extends: [
                  'standard',
                  'plugin:vue/recommended',
                  'prettier/standard',      // 覆蓋 eslint-config-stanard 
                  'prettier/vue',           // 覆蓋 eslint-plugin-vue 
              ],
          }

          提示:在 eslint-config-prettier 8.0.0 版本后,extends 不再需要為單獨(dú)的插件引入對應(yīng)擴(kuò)展來覆蓋沖突了,統(tǒng)一引入 'prettier' 即可。

          如果同時(shí)使用了 eslint-plugin-prettier 和 eslint-config-prettier 可以這么配置:

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

          它其實(shí)和下面這些配置是等價(jià)的:

          {
              extends: ['prettier'],  // eslint-config-prettier 提供的,用于覆蓋起沖突的規(guī)則
              plugins: ['prettier'],  // 注冊 eslint-plugin-prettier 插件
              rules: {
                  'prettier/prettier''error',
                  'arrow-body-style''off',
                  'prefer-arrow-callback''off'
              }
          }

          所以如果是在 Vue 2 項(xiàng)目中配置 ESLint 和 Prettier 會(huì)這么配置:

          {
              parser'vue-eslint-parser',
              parseOptions: {
                  parser'babel-eslint',
                  ecmaVersion12,
                  sourceType'module'
              },
              extends: [
                  'eslint:recommended'
                  'plugin:vue/recommended',
                  'plugin:prettier/recommended',  // 在前面 Vue 配置的基礎(chǔ)上加上這行
              ],
              plugins: ['vue']
          }

          其實(shí)如果你的項(xiàng)目是用 vue-cli 初始化的,且選擇了 eslint + prettier 方案的話,生成的項(xiàng)目中,.eslintrc.js 配置文件中 extends 的配置是這樣的:

          {
              extends: [
                  'plugin:vue/essential',
                  'eslint:recommended'
                  '@vue/prettier'
              ]
          }

          它的最后一項(xiàng)擴(kuò)展是 @vue/prettier,這個(gè)對應(yīng)的是 @vue/eslint-config-prettier 這個(gè)包,讓我們看看這個(gè)包下面的 index.js 內(nèi)容:

          {
              plugins: ['prettier'],
              extends: [
                  require.resolve('eslint-config-prettier'),
                  require.resolve('eslint-config-prettier/vue')
              ],
              rules: {
                  'prettier/prettier''warn'
              }
          }

          這個(gè)和我們上面配置的內(nèi)容是相差無幾的,而引入 eslint-config-prettier/vue 是因?yàn)檫@個(gè) @vue/eslint-config-prettier 包依賴的 eslint-config-prettier 版本是 ^6.0.0 版本的,所以在處理沖突的時(shí)候需要特別指定和對應(yīng)類型插件匹配的擴(kuò)展。

          讓 EditorConfig 助力多編輯器開發(fā)吧

          EditorConfig[16] 是個(gè)啥玩意?它可以對多種類型的單文件進(jìn)行簡單的格式化,它提供的配置參數(shù)很少:

          # 告訴 EditorConfig 插件,這是根文件,不用繼續(xù)往上查找
          root = true

          # 匹配全部文件
          [*]

          # 設(shè)置字符集
          charset = utf-8

          # 縮進(jìn)風(fēng)格,可選 space、tab
          indent_style = tab

          # 縮進(jìn)的空格數(shù),當(dāng) indent_style = tab 將使用 tab_width
          # 否則使用 indent_size
          indent_size = 2
          tab_width = 2

          # 結(jié)尾換行符,可選 lf、cr、crlf
          end_of_line = lf

          # 在文件結(jié)尾插入新行
          insert_final_newline = true

          # 刪除一行中的前后空格
          trim_trailing_whitespace = true

          # 匹配md結(jié)尾的文件
          [*.md]
          insert_final_newline = false
          trim_trailing_whitespace = false

          雖然它提供的格式化的配置參數(shù)很少,就 3 個(gè),縮進(jìn)風(fēng)格、是否在文件末尾插入新行和是否刪除一行中前后空格。但是它還是非常有必要存在的,理由有 3 個(gè):

          • 能夠在不同的編輯器和 IDE 中保持一致的代碼風(fēng)格;
          • 配合插件打開文件即自動(dòng)格式化,非常方便
          • 支持格式化的文件類型很多;

          如果需要讓以上的配置生效,還得在 VSCode 里安裝 EditorConfig for VS Code 這個(gè)插件配合使用。

          重點(diǎn)來了

          可以看到 EditorConfig 和 Prettier 會(huì)存在一些重復(fù)的配置,比如都提供了對縮進(jìn)的配置參數(shù),所以在實(shí)際使用的時(shí)候需要避免它們,或者把他們的參數(shù)設(shè)置為一致。

          在 VSCode 中支持 ESLint

          前面做的配置,都需要執(zhí)行命令才能進(jìn)行檢查和修復(fù)代碼,還是挺不方便的,如果我希望編輯完或者保存的時(shí)候去檢查代碼該如何做呢?可以直接在 IDE 里安裝 ESLint 插件,因?yàn)槲沂褂玫氖?VSCode,所以這里只介紹在 VSCode 中的配置。

          在使用前,需要把 ESLint 擴(kuò)展安裝到 VSCode 里,這里我就不細(xì)說安裝步驟了。安裝完成后,需要在設(shè)置里寫入配置:

          • 在 VSCode 左下角找到一個(gè)齒輪 ? 圖標(biāo),點(diǎn)擊后選擇設(shè)置選項(xiàng),這個(gè)時(shí)候打開了設(shè)置面板;
          • 然后在 VSCode 右上角找到打開設(shè)置(json)的圖標(biāo),點(diǎn)擊后,會(huì)打開 settings.json 文件;
          • 然后把以下配置貼進(jìn)去即可;
          {
              "eslint.alwaysShowStatus"true,  // 總是在 VSCode 顯示 ESLint 的狀態(tài)
              "eslint.quiet"true,             // 忽略 warning 的錯(cuò)誤
              "editor.codeActionsOnSave": {     // 保存時(shí)使用 ESLint 修復(fù)可修復(fù)錯(cuò)誤
                  "source.fixAll"true,
                  "source.fixAll.eslint"true
              } 
          }

          配置說明,在 ESLint 2.0.4 版本開始:

          • 不需要通過 eslint.validate 來指定校驗(yàn)的文件類型了,已經(jīng)自動(dòng)支持了 .vue 文件;
          • editor.codeActionsOnSave 開啟保存自動(dòng)修復(fù)功能;

          當(dāng)這樣配置之后呢,每次編輯代碼 ESLint 都會(huì)實(shí)時(shí)校驗(yàn)代碼,且當(dāng)保存的時(shí)候會(huì)自動(dòng) fix,是不是很方便呢。不過對于有些無法自動(dòng) fix 的代碼就需要你手動(dòng)去修改了,如果不想修改的話就可以配置 rules 把該條規(guī)則給關(guān)閉掉。

          其實(shí)在團(tuán)隊(duì)開發(fā)的時(shí)候,最好把針對 VSCode 的配置,寫一個(gè)文件跟隨著項(xiàng)目,一起提交到遠(yuǎn)程倉庫,這樣的話就保證了項(xiàng)目成員都是用的這套配置。比如可以在項(xiàng)目根目錄新建 .vscode/settings.json,然后寫入上面的那串配置內(nèi)容。

          在提交前做校驗(yàn) pre-commit

          以上只是通過 ESLint 自動(dòng)修復(fù)能夠修復(fù)的錯(cuò)誤以及通過 Prettier 進(jìn)行代碼的格式化,但是在實(shí)際開發(fā)的時(shí)候難免會(huì)遇到無法 fix 的錯(cuò)誤,可能開發(fā)人員也忘記修改,如果這個(gè)時(shí)候把代碼提交到遠(yuǎn)程倉庫,那就把糟糕的代碼給提交上去了。

          那么如何杜絕把糟糕的代碼提交上去呢?可以通過配置 git hooks 的 pre-commit 鉤子來實(shí)現(xiàn)這個(gè)目的。主要是利用了 husky[17]lint-staged[18] 這 2 個(gè)包。husky 就是用來配置 git hooks 的,而 lint-staged 則是對拿到的 staged 文件進(jìn)行處理,比如執(zhí)行 npx eslint --fix 進(jìn)行代碼校驗(yàn)。

          具體操作步驟如下:

          1、執(zhí)行以下命令:

          npx mrm lint-staged

          會(huì)自動(dòng)安裝 lint-staged 和 husky 并且在 package.json 里寫入 lint-staged。

          注意:mrm 是一個(gè)自動(dòng)化工具,它將根據(jù) package.json 依賴項(xiàng)中的代碼質(zhì)量工具來安裝和配置 husky 和 lint-staged,因此請確保在此之前安裝并配置所有代碼質(zhì)量工具,如 Prettier 和 ESlint。

          如果上面順利會(huì)在 package.json 里寫入 lint-staged,可以自行修改讓它支持 .vue 文件的校驗(yàn):

          {
              "lint-staged": {
                  "*.{js,vue}""eslint --cache --fix"
              }
          }

          2、啟動(dòng) git hooks

          npx husky install

          經(jīng)過上面的命令后,v6 版本的 husky 會(huì)在項(xiàng)目根目錄新建一個(gè) .husky 目錄。如果是 v4 版本的則會(huì)寫入到 package.json 里。

          3、創(chuàng)建 pre-commit 鉤子

          npx husky add .husky/pre-commit "npx lint-staged"

          到這里后,git commit 前自動(dòng)執(zhí)行代碼校驗(yàn)和修復(fù)的功能就算完成了。然后你可以試試修改文件,然后提交試試。

          總結(jié)

          這篇文章比較長,前前后后講了很多代碼校驗(yàn)的東西,現(xiàn)在我們來梳理下。

          首先用 ESLint 來做代碼校驗(yàn),它自帶的 ruels 能提供 2 種類型的校驗(yàn),分別是代碼錯(cuò)誤校驗(yàn)和代碼格式校驗(yàn),而 ESLint 本身的核心工作其實(shí)就是校驗(yàn)和修復(fù)錯(cuò)誤的代碼,而對格式化的規(guī)則提供的不多。

          所以如果想要對代碼格式化進(jìn)行一個(gè)更加精細(xì)的配置則需要借助 Prettier,因?yàn)樗侵回?fù)責(zé)風(fēng)格的管控,所以用它再適合不過了。但是如果把 ESLint 和 Prettier 結(jié)合起來一起使用的話,就可能會(huì)出現(xiàn)規(guī)則的沖突了,畢竟它們兩者都會(huì)對風(fēng)格進(jìn)行處理,所以這個(gè)時(shí)候就可以通過 eslint-config-prettier 這個(gè)擴(kuò)展來把沖突的規(guī)則進(jìn)行關(guān)閉,這個(gè)擴(kuò)展不僅可以關(guān)閉和 ESLint 內(nèi)置規(guī)則的沖突,還可以關(guān)閉實(shí)際項(xiàng)目中引用到的擴(kuò)展規(guī)則的沖突,比如和 Vue、React、TypeScript、Flow 的沖突。

          在把 ESLint 和 Prettier 結(jié)合的時(shí)候,我們希望讓 ESLint 來檢查代碼錯(cuò)誤,而 Prettier 校驗(yàn)代碼風(fēng)格,那么這個(gè)時(shí)候其實(shí)是有 2 個(gè)任務(wù)的,需要用 2 條命令來處理的。但是有了 eslint-plugin-prettier 這個(gè)插件后就可以很方便的把它們結(jié)合起來,當(dāng)需要校驗(yàn)代碼錯(cuò)誤的時(shí)候 ESLint 自動(dòng)會(huì)給你校驗(yàn),當(dāng)然前提是 VSCode 里必須按照 ESLint 插件,而當(dāng)需要校驗(yàn)代碼風(fēng)格的時(shí)候 ESLint 就會(huì)調(diào)用 Prettier 的能力進(jìn)行代碼風(fēng)格的檢查。

          文章的后面分別又細(xì)說了 EditorConfig 和提交代碼前校驗(yàn)的處理,這里就不多講了。

          看到這里希望你對代碼校驗(yàn)和規(guī)范有一個(gè)新的認(rèn)識,不過我最希望的是你能夠自己動(dòng)手為你的項(xiàng)目配置一套校驗(yàn)規(guī)則,如果不能成功,一定是我的文章寫的有問題,歡迎評論區(qū)留言指出不足之處,我是大海我來了,下篇文章見。

          參考資料

          [1]

          Esprima: http://esprima.org/

          [2]

          Espree: https://github.com/eslint/espree

          [3]

          @babel/eslint-parser: https://github.com/babel/babel/tree/main/eslint/babel-eslint-parser

          [4]

          @typescript-eslint/parser: https://github.com/typescript-eslint/typescript-eslint

          [5]

          Specifying Environments: https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments

          [6]

          react .eslintrc.js: https://github.com/facebook/react/blob/master/.eslintrc.js

          [7]

          eslint-config-standard: https://github.com/standard/eslint-config-standard

          [8]

          eslint-config-prettier: https://github.com/prettier/eslint-config-prettier

          [9]

          eslint-config-alloy: https://github.com/AlloyTeam/eslint-config-alloy

          [10]

          ESLint plugins: https://eslint.org/docs/user-guide/configuring/plugins#configuring-plugins

          [11]

          ESLint配置文件.eslintrc參數(shù)說明: https://gist.github.com/rswanderer/29dc65efc421b3b5b0442f1bd3dcd046

          [12]

          webpack .eslintrc.js: https://github.com/webpack/webpack/blob/master/.eslintrc.js

          [13]

          eslint-plugin-vue: https://eslint.vuejs.org/user-guide/#faq

          [14]

          prettier options: https://prettier.io/docs/en/options.html#print-width

          [15]

          Trailing commas: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas#trailing_commas_in_functions

          [16]

          EditorConfig: https://editorconfig.org/

          [17]

          husky: https://typicode.github.io/husky/#/?id=install

          [18]

          lint-staged: https://github.com/okonet/lint-staged


          瀏覽 135
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人操,人人 | 亚洲免费黄色视频 | 偷拍精品一区二区三区 | 做爰 视频毛片下载蜜桃视频。 黄网免费在线观看 | 成人精品一区二三区 |