There are a thousand Hamlets in a thousand people's eyes.
一千個程序員,就有一千種代碼風格。在前端開發中,有幾個至今還在爭論的代碼風格差別:javascript
這幾個代碼風格差別在協同開發中常常會被互相吐槽,甚至不能忍受。前端
除此以外,因爲 JavaScript 的靈活性,每每一段代碼能有多種寫法,這時候也會致使協同時差別。而且,有一些寫法可能會致使不易發現的 bug,或者這些寫法的性能很差,開發時也應該避免。vue
爲了解決這類靜態代碼問題,每一個團隊都須要一個統一的 JavaScript 代碼規範,團隊成員都遵照這份代碼規範來編寫代碼。固然,靠人來保障代碼規範是不可靠的,須要有對應的工具來保障,ESLint 就是這個工具。java
有的讀者看到這裏,可能會說:Prettier 也能夠保證代碼風格一致。是的,Prettier 確實能夠按照設置的規則對代碼進行統一格式化,後面的文章也會有對應的介紹。可是須要明確的一點是,Prettier 只會在格式上對代碼進行格式化,一些隱藏的代碼質量問題 Prettier 是沒法發現的,而 ESLint 能夠。node
關於 ESLint,它的 Slogan 是 Find and fix problems in your JavaScript code。如上文所說,它能夠發現並修復你 JavaScript 代碼中的問題。來看一下官網上描述 ESLint 具有的三個特性:react
基於以上描述,咱們在前端工程化中能夠這樣使用 ESLint:git
先簡單介紹一下如何使用 ESLint,若是已經有所瞭解的同窗,能夠直接跳過這一節。程序員
新建一個包含 package.json
的目錄(能夠在空目錄下執行 npm init -y
),新建一個 index.js
:github
// index.js const name = 'axuebin'
安裝 eslint
:typescript
npm install eslint --save-dev
而後執行 ./node_modules/.bin/eslint --init
或者 npx eslint --init
生成一個 ESLint 配置文件 .eslintc.js
:
module.exports = { env: { es2021: true, }, extends: 'eslint:recommended', parserOptions: { ecmaVersion: 12, }, rules: {}, };
生成好配置文件以後,就能夠執行 ./node_modules/.bin/eslint index.js
或者 npx eslint index.js
命令對文件進行檢查。結果以下:index.js
中的代碼命中了 no-unused-vars
這個規則,默認狀況下,這個規則是會報 error
的,也就是 ESLint 不容許代碼中出現未被使用的變量。這是一個好習慣,有利於代碼的維護。
咱們來嘗試配置 ESLint 的檢查規則。以分號和引號舉例,如今你做爲團隊代碼規範的指定人,但願團隊成員開發的代碼,都是單引號和帶分號的。
打開 .eslintrc.js
配置文件,在 rules
中添加相關配置項:
module.exports = { env: { es2021: true, }, extends: 'eslint:recommended', parserOptions: { ecmaVersion: 12, }, rules: { semi: ['error', 'always'], quotes: ['error', 'single'], }, };
而後咱們將 index.js
中的代碼改爲:
// index.js const name = "axuebin"
執行 eslint
命令以後:
能夠看到檢查結果以下:
老老實實地按照規範修改代碼,使用單引號並將加上分號。固然,若是大家但願是雙引號和不帶分號,修改相應的配置便可。
具體各個規則如何配置能夠查看:https://eslint.org/docs/rules
執行 eslint xxx --fix
能夠自動修復一些代碼中的問題,將沒法自動修復的問題暴露出來。好比上文中提到的引號和分號的問題,就能夠經過 --fix
自動修復,而 no-unused-vars
變量未使用的問題,ESLint 就沒法自動修復。
在 init
生成的配置文件中,咱們看到包含這一行代碼:
module.exports = { extends: "eslint:recommended" }
這一行代碼的意思是,使用 ESLint 的推薦配置。 extends: 'xxx'
就是 繼承,當前的配置繼承於 xxx
的配置,在此基礎上進行擴展。
所以,咱們也可使用任意封裝好的配置,能夠在 NPM 上或者 GItHub 上搜索 eslint-config
關鍵詞獲取,本文咱們將這類封裝好的配置稱做 「配置集」。比較常見的配置包有如下幾個:
簡單瞭解完 ESLint 以後,對於 ESLint 的更多使用細節以及原理,在本篇文章就不展開了,感興趣的朋友能夠在官網詳細瞭解。本文重點仍是在於如何在團隊工程化體系中落地 ESLint,這裏提幾個最佳實踐。
對於獨立開發者以及業務場景比較簡單的小型團隊而言,使用現成、完備的第三方配置集是很是高效的,能夠較低成本低接入 ESLint 代碼檢查。
可是,對於中大型團隊而言,在實際代碼規範落地的過程當中咱們會發現,不可能存在一個可以徹底符合團隊風格的三方配置包,咱們仍是會在 extends
三方配置集的基礎上,再手動在 rules
配置里加一些自定義的規則。時間長了,有可能 A 應用和 B 應用裏的 rules
就不同了,就很難達到統一的目的。
這時候,就須要一箇中心化的方式來管理配置包:根據團隊代碼風格整理(或者基於現有的三方配置集)發佈一個配置集,團隊統一使用這個包,就能夠作到中心化管理和更新。
除此以外,從技術層面考慮,目前一個前端團隊的面對的場景可能比較複雜。好比:
以上問題在真實開發中都是存在的,因此在代碼規範的工程化方案落地時,一個單一功能的配置集是不夠用的,這時候還須要考慮這個配置集如何抽象。
爲了解決以上問題,這裏提供一種解決方案的思路:
具體拆解來看,就是有一個相似 eslint-config-standard 的基礎規則集(包括代碼風格、變量相關、ES6 語法等),在此基礎之上集成社區的一些插件(Vue/React)等,封裝成統一的一個 NPM Package 發佈,消費時根據當前應用類型經過不一樣路徑來 extends 對應的配置集。
這裏有一個 Demo,感興趣的朋友能夠看一下:eslint-config-axuebin
ESLint 提供了豐富的配置供開發者選擇,可是在複雜的業務場景和特定的技術棧下,這些通用規則是不夠用的。ESLint 經過插件的形式賦予了擴展性,開發者能夠自定義任意的檢查規則,好比 eslint-plugin-vue / eslint-plugin-react 就是 Vue / React 框架中使用的擴展插件,官網也提供了相關文檔引導開發者開發一個插件。
通常來講,咱們也不須要開發插件,但咱們至少須要瞭解有這麼個東西。在作一些團隊代碼質量檢查的時候,咱們可能會有一些特殊的業務邏輯,這時候 ESLint 插件是能夠幫助咱們作一些事情。
這裏就不展開了,主要就是一些 AST 的用法,照着官方文檔就能夠上手,或者能夠參考現有的一些插件寫法。
當有了團隊的統一 ESLint 配置集和插件以後,咱們會將它們集成到腳手架中,方便新項目集成和開箱即用。可是對於一些老項目,若是須要手動改造仍是會有一些麻煩的,這時候就能夠藉助於 CLI 來完成一鍵升級。
本文結合上文的 Demo eslint-config-axuebin,設計一個簡單的 CLI Demo。因爲當前配置也比較簡單,因此 CLI 只須要作幾件簡單的事情便可:
.eslintrc.js
文件package.json
的 scripts
中寫入 "lint": "eslint src test --fix"
核心代碼以下:
const path = require('path'); const fs = require('fs'); const chalk = require('chalk'); const spawn = require('cross-spawn'); const { askForLanguage, askForFrame } = require('./ask'); const { eslintrcConfig, needDeps } = require('./config'); module.exports = async () => { const language = await askForLanguage(); const frame = await askForFrame(); let type = language; if (frame) { type += `/${frame}`; } fs.writeFileSync( path.join(process.cwd(), '.eslintrc.js'), `// Documentation\n// https://github.com/axuebin/eslint-config-axuebin\nmodule.exports = ${JSON.stringify( eslintrcConfig(type), null, 2 )}` ); const deps = needDeps.javascript; if (language === 'typescript') { deps.concat(needDeps.typescript); } if (frame) { deps.concat(needDeps[frame]); } spawn.sync('npm', ['install', ...deps, '--save'], { stdio: 'inherit' }); };
可運行的 CLI Demo 代碼見:axb-lint,在項目目錄下執行:axblint eslint
便可,如圖:
配置了 ESLint 以後,咱們須要讓開發者感知到 ESLint 的約束。開發者能夠本身運行 eslint 命令來跑代碼檢查,這不夠高效,因此咱們須要一些自動化手段來作這個事情。固然 在開發時,編輯器也有提供相應的功能能夠根據當前工做區下的 ESLint 配置文件來檢查當前正在編輯的文件,這個不是咱們關心的重點。
通常咱們會在有如下幾種方式作 ESLint 檢查:
這裏提一下 pre-commit 的方案,在每一次本地開發完成提交代碼前就作 ESLint 檢查,保證雲端的代碼是統一規範的。
這種方式很是簡單,只須要在項目中依賴 husky 和 lint-staged 便可完成。安裝好依賴以後,在 package.json 文件加入如下配置便可:
{ "lint-staged": { "*.{js,jsx,ts,tsx}": "eslint --cache --fix" }, "husky": { "hooks": { "pre-commit": "lint-staged" } } }
效果如圖所示:
若是代碼跑 ESLint 檢查拋了 Error 錯誤,則會中斷 commit 流程:
這樣就能夠確保提交到 GitHub 倉庫上的代碼是統一規範的。(固然,若是認爲將這些配置文件都刪了,那也是沒辦法的)
本文介紹了 ESLint 在中小型前端團隊的一些最佳實踐的想法,你們能夠在此基礎上擴展,制訂一套完善的 ESLint 工做流,落地到本身團隊中。
本文是前端代碼規範系列文章的其中一篇,後續還有關於 StyleLint/CommitLint/Prettier 等的文章,而且還有一篇完整的關於前端代碼規範工程化實踐的文章,敬請期待(也有可能就鴿了)。
更多原創文章歡迎關注公衆號「玩相機的程序員」,或者加我微信 xb9207 交流