從 0-1 配置 eslint + prettier
前言:為什么需要項(xiàng)目規(guī)范
我們學(xué)習(xí)編程的方式各有不同,對于知識的理解也各有不同,在一天天的編程過后,每個(gè)人都養(yǎng)成了自己的代碼習(xí)慣和理解。
代碼習(xí)慣和理解的差異,導(dǎo)致了團(tuán)隊(duì)中會(huì)出現(xiàn)各種各樣的 “規(guī)范” 代碼。在你查看自己的代碼時(shí),你可能會(huì)覺得自己的代碼看起來比較標(biāo)準(zhǔn),只是有點(diǎn)亂。但是在團(tuán)隊(duì)成員查看你的代碼時(shí),他心里可能會(huì)這么想:wtf,他寫的代碼怎么是這個(gè)樣子。這種風(fēng)格的代碼就好像是一個(gè)公司律師用 excel 規(guī)范自動(dòng)格式化的沙拉食譜,看起來一點(diǎn)都不靠譜。
這種差異性導(dǎo)致了團(tuán)隊(duì)協(xié)作的效率低下,也影響了項(xiàng)目的健壯性和可維護(hù)性。所以,我們需要對代碼風(fēng)格進(jìn)行規(guī)范。這種規(guī)范不僅可以使代碼風(fēng)格保持統(tǒng)一,并且可以在代碼運(yùn)行之前就檢測出一些錯(cuò)誤和 bug,提高協(xié)作開發(fā)效率。
而前端開發(fā)人員最常用的 Javascript 最初設(shè)計(jì)出來是只是為了解決一些簡單的網(wǎng)頁互動(dòng),是一種弱類型、基于原型的語言。
Javascript 擁有其它語言所沒有的靈活性,這種靈活性帶來了代碼效率的提升,但相應(yīng)也使得代碼編寫具有很大的隨意性。另外 Javascript 的隱式類型轉(zhuǎn)換規(guī)則混亂,允許同名函數(shù)的重復(fù)定義,這就增加了代碼中存在隱患的可能性。
冷笑話:Javascript 權(quán)威指南和 Javascript 語言精粹的厚度區(qū)別。
如果能夠在代碼提交測試之前發(fā)現(xiàn)這些潛在的錯(cuò)誤,就能夠極大地減輕測試人員的壓力,減少軟件項(xiàng)目的除錯(cuò)成本。可是 Javascript 作為解釋型語言,解釋器被內(nèi)嵌在對應(yīng)的客戶端,對此表示無能為力,這個(gè)任務(wù)只能由專用的代碼檢查工具完成。
在接下來,我會(huì)針對上述的這些問題展開講述,首先介紹解決這些問題的工具,然后再介紹借助這項(xiàng)工具來解決團(tuán)隊(duì)規(guī)范和錯(cuò)誤預(yù)檢問題的步驟方法,最后再用一行命令來整合這些復(fù)雜的步驟,將使用門檻降到最低。
Eslint 和 Prettier
lint
lint 是最著名的 C 語言工具之一,是由貝爾實(shí)驗(yàn)室 SteveJohnson 于 1979 在 PCC(PortableC Compiler) 基礎(chǔ)上開發(fā)的靜態(tài)代碼分析,一般由 UNIX 系統(tǒng)提供。
lint 被用于檢查 C 程序中潛在的錯(cuò)誤,包括(但不限于)可疑的類型組合、未使用的變量、不可達(dá)的代碼以及不可移植的代碼。lint會(huì)產(chǎn)生一系列程序員有必要從頭到尾仔細(xì)閱讀的診斷信息。使用lint的好處是:
-
它可以檢查出被編譯器漏掉的錯(cuò)誤; -
可以關(guān)聯(lián)很多文件進(jìn)行錯(cuò)誤的檢查和代碼分析,具有較強(qiáng)大靈活性
lint 應(yīng)該也算是 lint 界的老祖宗了。
Eslint
有個(gè)叫 Nicholas C. Zakas 的人在 2013 年推出了一個(gè) Javascript 的 lint 工具,而 Javascript 也稱作 ECMAScript(簡稱 ES),所以這個(gè)工具被叫做 ESlint。
ESLint 是在 ECMAScript/JavaScript 代碼中識別和報(bào)告模式匹配的工具,它的目標(biāo)是保證代碼的一致性和避免錯(cuò)誤。
Eslint 可以在運(yùn)行代碼前就發(fā)現(xiàn)一些語法錯(cuò)誤和潛在的 bug,極大地減輕測試人員的壓力,減少軟件項(xiàng)目的除錯(cuò)成本。同時(shí),Eslint 允許開發(fā)者通過 rules 定義自己的代碼規(guī)范,所以非常適合用于制定團(tuán)隊(duì)代碼規(guī)范。
Prettier
Prettier 是一款代碼格式化工具,用于檢測代碼中的格式問題,比如單行代碼長度、tab長度、空格、逗號表達(dá)式等。在功能職責(zé)上,ESlint 偏向于把控項(xiàng)目的代碼質(zhì)量,而 Prettier 更偏向于統(tǒng)一項(xiàng)目的編碼風(fēng)格。
在 ESlint 推出 --fix 參數(shù)前,ESLint 并沒有自動(dòng)化格式代碼的功能,要對一些格式問題做批量格式化只能用 Prettier 這樣的工具。并且,Prettier 在代碼風(fēng)格的檢測上比 ESlint 更全面,所以兩者通常是結(jié)合在一起使用的。
常見的標(biāo)準(zhǔn)規(guī)范
在介紹規(guī)范之前,可以先使用 npm i eslint prettier \-g 命令全局安裝 eslint 和 prettier,在后面的教程中都會(huì)使用到這兩個(gè)全局包。
ESlint 推薦的規(guī)范
ESlint 在默認(rèn)情況下是不開啟任何自定義規(guī)則校驗(yàn),只對錯(cuò)誤的 ES5 語法和標(biāo)準(zhǔn)的語法錯(cuò)誤進(jìn)行檢測,比如 const 這種 ES6 語法,還有莫名其妙的分號(如下圖)。
當(dāng)我們在項(xiàng)目目錄下新增 eslintrc.js 文件,并寫入以下內(nèi)容后,將會(huì)啟用 ESlint 推薦的規(guī)范:
module.exports = {
root: true,
extends: 'eslint:recommended'
};
復(fù)制代碼
在 ESlint 的推薦規(guī)范中,會(huì)有一些內(nèi)置的規(guī)則,比如定義后未使用的變量將會(huì)拋出錯(cuò)誤,使用常量作為循環(huán)條件也會(huì)拋出錯(cuò)誤(如下圖)。
ESlint 的推薦規(guī)范可以避免掉一些錯(cuò)誤,比如上述兩個(gè)錯(cuò)誤就可以在運(yùn)行前被檢查到并解決,更多詳細(xì)規(guī)范請參考 ESlint Recommend。
standard
standard 是基于 ESlint Recommend 衍生出來的更嚴(yán)格的規(guī)范。這個(gè)規(guī)范和 recommended 大概有 88 處不同,主要是 recommended 很多都是 off, standard 是 error, 比如 單行代碼塊兩邊加空格、禁止使用分號結(jié)尾。
下面的代碼在 recommended 規(guī)范下不會(huì)報(bào)錯(cuò),而在 standard 規(guī)范中會(huì)報(bào)錯(cuò)。
recommended 規(guī)范
standard 規(guī)范
先使用 npm i standard eslint-plugin-standard eslint-config-standard \-D 命令安裝 standard 插件,然后在 eslintrc.js 文件中寫入以下內(nèi)容后,將會(huì)啟用 standard 規(guī)范:
module.exports = {
root: true,
extends: ['standard']
};
復(fù)制代碼
standard 會(huì)比 recommended 更加嚴(yán)格,在代碼風(fēng)格上也做了一些限制。不過它的用戶群體也是比較多的,也不乏一些大家耳熟能詳?shù)?。(如下圖)
詳細(xì)規(guī)范請參考 ESlint Standard。
airbnb
airbnb 規(guī)范是最嚴(yán)格的 ESlint 規(guī)范,列出下面幾點(diǎn)比較明顯的區(qū)別:
-
默認(rèn)必須要分號,而eslint默認(rèn)不添加分號 -
不能使用for循環(huán),推薦使用數(shù)組自帶的 API 完成遍歷工作。 -
當(dāng)你必須使用函數(shù)表達(dá)式(或傳遞一個(gè)匿名函數(shù))時(shí),使用箭頭函數(shù)符號。
除了這些以外,還有更多嚴(yán)格的規(guī)則,可以查看 Airbnb 規(guī)范。
在項(xiàng)目中配置 Eslint + Prettier
由于 Eslint 和 Prettier 存在一些相同的規(guī)則,當(dāng)同一個(gè)規(guī)則設(shè)置不同時(shí),就會(huì)出現(xiàn)很詭異的現(xiàn)象:使用 prettier 格式化的代碼,無法通過 eslint 校驗(yàn)。
下面,我們就以一份 前端代碼規(guī)范 為例,為一個(gè)項(xiàng)目配置一套完整的 ESlint + Prettier 規(guī)范。
配置 .eslintrc.js
我們新建一個(gè) eslintrc.js 文件,寫入以下內(nèi)容,作為我們的初始化配置。(如下)
module.exports = {
root: true // 表示該文件為根配置文件
};
復(fù)制代碼
在上一章的示例代碼中,我們發(fā)現(xiàn) eslint 默認(rèn)只能識別 es5 的語法,所以我們先配置 env 屬性,讓 eslint 支持到 es6 語法,并且我們設(shè)置環(huán)境為 browser(瀏覽器) 或 node(如下)。
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true,
},
extends: 'eslint:recommended'
};
復(fù)制代碼
如果使用 standard 標(biāo)準(zhǔn)則不需要額外設(shè)置,standard 支持最新的 ECMAScript 特性。而實(shí)驗(yàn)性的特性,則需要添加 babel-eslint 解析器。
所以,這里我們直接配置 standard 標(biāo)準(zhǔn)和 babel-eslint 解析器,再加上一些自定義規(guī)則后,最后配置的規(guī)則如下:
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint', // 解析一些最新的 es 語法
sourceType: 'module' // 模塊為 ECMAScript 模塊
},
extends: ['standard'], // 使用 standard 標(biāo)準(zhǔn)
rules: {
'no-debugger': 'error', // 禁止在代碼中使用 debugger
quotes: ['error', 'single'], // 單引號
semi: ['error', 'always'] // 代碼需要以分號結(jié)尾
}
};
復(fù)制代碼
在 ESlint 規(guī)范文件配置完成后,我們再來添加 Prettier 配置文件,新建 .prettierrc.js 文件,添加以下內(nèi)容:
module.exports = {
printWidth: 800, // 單行寬度限制
tabWidth: 2, // tab 使用兩個(gè)空格
useTabs: false, // 不使用制表符縮進(jìn),使用空格縮進(jìn)
semi: true, // 代碼需要分號結(jié)尾
singleQuote: true, // 單引號
bracketSpacing: true, // 對象左右兩側(cè)需要空格
jsxBracketSameLine: false, // html 關(guān)閉標(biāo)簽換行
arrowParens: 'avoid', // 單參數(shù)的箭頭函數(shù)參數(shù)不需要括號
proseWrap: 'never', // 參考 https://prettier.io/docs/en/options.html#prose-wrap
trailingComma: 'none' // 參考 https://prettier.io/docs/en/options.html#trailing-commas
};
復(fù)制代碼
在配置完成后,我們新建一個(gè)文件,測試一下效果。在 src 目錄下新建文件 test.js,填入以下內(nèi)容:
使用自動(dòng)格式化
從上面可以看出,文件并不符合我們制定的 eslint 規(guī)范,我們下面分別使用 eslint 格式化和 prettier 格式化功能來嘗試修正。
首先,我們在命令行輸入 eslint \--fix src/test.js,然后看看效果(如下圖)
我們可以看到,多余的空格被刪除了,雙引號被換成了雙引號,賦值運(yùn)算符的左右兩側(cè)也被加上了空格。
接下來,我們先把文件還原,然后使用 prettier \-w src/test.js 命令進(jìn)行格式化,效果如下圖:
從上圖可以看出,由于我們設(shè)置的 eslint 和 prettier 的規(guī)則一致,所以格式化后的文件也是高度相似的。
這樣一來,我們就完成了代碼規(guī)范格式的統(tǒng)一。
vscode 插件
大家從上面的示例代碼中可以看出,編寫不合規(guī)范的代碼會(huì)直接在編輯器中報(bào)出錯(cuò)誤,這是因?yàn)樯厦娴氖纠a使用 vscode 展示,并安裝了一些插件來輔助開發(fā)。
下面,我們就來介紹一下這些插件。
我們先通過 vscode 安裝 ESLint 插件(如下圖)
在安裝了 ESLint 插件后,插件會(huì)讀取目錄下的 eslint 配置文件,然后對代碼中的錯(cuò)誤進(jìn)行檢查后提示出來(如下圖)。
如果我們在 vscode 中設(shè)置了下面這個(gè)屬性的話,在保存文件的時(shí)候?qū)?huì)自動(dòng)格式化代碼。(如下圖)
下面,我們可以再安裝 Prettier 插件(如下圖)。
在安裝好插件后,使用鍵盤組合鍵 shift + command/ctrl + p 喚起設(shè)置,然后輸入 Format Selection With... 后,按回車鍵,在選項(xiàng)中選擇 Prettier 即可(如下圖)。
在設(shè)置完成后,使用組合鍵 shift + option/alt + f 即可完成對文件的格式化。
這兩個(gè)插件還有更多的功能,大家可以自行探索一下。
插件的三兩事
如果你對 vscode 的插件比較熟悉,可以跳過本章節(jié)內(nèi)容。
在 vscode 的插件安裝過程中,有很多童鞋反饋過問題,這里給出一些常見問題的解決方案。
插件不工作
-
全局安裝 npm i eslint prettier -g -
安裝好 vscode插件后,重啟vscode -
如果還是不行的話,升級 vscode
插件未啟用
新版 vscode 需要手動(dòng)啟用 eslint 插件,在右下角查看 eslint 工作狀態(tài),可以點(diǎn)擊開啟。(如下圖)
啟用后不工作
右下角查看 eslint 工作狀態(tài),點(diǎn)擊會(huì)輸出日志。(如下圖)
根據(jù)輸出日志,進(jìn)行修復(fù),比如上圖就是缺少對應(yīng)插件,安裝即可。
保存沒有按照 eslint 的規(guī)則修復(fù)
這可能是因?yàn)槟愕?vscode 開啟了保存自動(dòng)格式化(代碼格式化),先打開 首選項(xiàng) > 設(shè)置,搜索 format on save,然后關(guān)閉這個(gè)選項(xiàng)(如下圖)
正常工作
正常工作的 eslint 和 prettier 插件狀態(tài)如下圖所示。
在提交時(shí)自動(dòng)檢測和格式化代碼
在項(xiàng)目開發(fā)過程中,自動(dòng)格式化并不總是讓人安心的,因?yàn)椴⒉皇琼?xiàng)目組的所有成員都會(huì)使用 vscode 插件來做自動(dòng)格式化。
這樣的情況會(huì)導(dǎo)致有一些不規(guī)范的代碼被提交到服務(wù)端,依然會(huì)造成團(tuán)隊(duì)規(guī)范不一致的問題,這個(gè)時(shí)候就需要用到提交時(shí)自動(dòng)檢測和格式化代碼的功能。
接下來,我們將使用 husky 來進(jìn)行代碼提交時(shí)的自動(dòng)檢測工作。
先使用 npm i husky \-D 安裝依賴,在依賴完成完成后,我們需要使用下面這條命令初始化 husky(如下)
npx husky install && npx husky set .husky/pre-commit "npm run pre-commit"
復(fù)制代碼
上面的命令是初始化 git hook,在 git commit 之前會(huì)先執(zhí)行 pre-commit 命令。
所以,我們還需要在項(xiàng)目的 package.json 中,添加 pre-commit,這個(gè)命令運(yùn)行時(shí)進(jìn)行 eslint 檢測(如下)。
"scripts": {
"pre-commit": "eslint src"
}
復(fù)制代碼
接下來,我們運(yùn)行 git add . 和 git commit \-m 'test' 命令,嘗試提交代碼,會(huì)發(fā)現(xiàn)提交失敗,命令行輸出如下圖。
如上圖所示,在提交時(shí)檢測到代碼不符合 eslint 規(guī)范,提交失敗了。
如果我們希望在檢測錯(cuò)誤的同時(shí),自動(dòng)修復(fù) eslint 語法錯(cuò)誤,則需要用到 lint-staged,使用 npm i lint-staged \-D 先進(jìn)行安裝,然后在 package.json 中修改 pre-commit 命令,再添加以下內(nèi)容。
"scripts": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"src/**": [
"eslint --fix"
]
}
復(fù)制代碼
接下來,我們再次運(yùn)行 git add . 和 git commit \-m 'test' 命令,嘗試提交代碼,輸出如下圖。
從上圖可以看出,在運(yùn)行 lint-staged 命令后,會(huì)通過 eslint \--fix 自動(dòng)將不符合規(guī)格的代碼正確格式化。
這樣一來,代碼提交時(shí)的自動(dòng)檢測和格式化代碼工作就完成了。
使用腳手架,將配置自動(dòng)化
手動(dòng)配置的問題
在完成上述配置后,似乎已經(jīng)大功告成,馬上可以走在規(guī)范編碼的愉快道路上,但是并沒有。
我們仔細(xì)梳理一下會(huì)發(fā)現(xiàn),我們需要在某個(gè)新項(xiàng)目中配置代碼規(guī)范時(shí),需要進(jìn)行以下繁瑣的步驟。
-
安裝 Eslint。 -
根據(jù)項(xiàng)目類型安裝對應(yīng)的 ESLint 規(guī)則配置 npm 包。 -
根據(jù)項(xiàng)目類型安裝相關(guān)的插件、解析器等。 -
根據(jù)項(xiàng)目類型配置 .eslintrc .prettierrc 文件。 -
安裝代碼提交檢查 + 自動(dòng)格式化工具。husky + lint-staged -
配置 package.json。 -
測試及修復(fù)問題。
這些繁瑣的步驟會(huì)耗費(fèi)大量的時(shí)間,并且可能還會(huì)出現(xiàn)一些錯(cuò)誤需要額外花時(shí)間去排查。這樣的流程對于個(gè)人來說可能是個(gè)比較好的學(xué)習(xí)機(jī)會(huì),但是對于團(tuán)隊(duì)來說確實(shí)是個(gè)低效的協(xié)作方式。
所以,我們可以借助一些工具來幫忙完成上述工作,這個(gè)工具可以根據(jù)配置選擇,生成對應(yīng)的規(guī)范配置,并安裝可以互相兼容的依賴包。
使用腳手架進(jìn)行自動(dòng)配置
我們先使用 npm i standard-config-cli \-g 命令全局安裝腳手架工具,然后在對應(yīng)的目錄下運(yùn)行 jgxl standard 命令。
這里我們以 vue + typescript 命令為例,選擇的配置如下圖。
在初始化完成后,對應(yīng)的幾個(gè)配置文件內(nèi)容如下:
.eslintrc.js
.prettierrc.js
package.json
從上面可以看出,我們的規(guī)范配置會(huì)根據(jù)所選配置,生成對應(yīng)的規(guī)范配置文件,并且已經(jīng)安裝了相關(guān)版本的依賴。
作為團(tuán)隊(duì)成員,不需要關(guān)心這些規(guī)范的細(xì)枝末節(jié),只需要進(jìn)行核心業(yè)務(wù)開發(fā)即可。
TIPS:
自動(dòng)修正功能只能修正部分代碼風(fēng)格規(guī)范,對于一些可能產(chǎn)生隱患的代碼問題不會(huì)自動(dòng)修正(例如:定義而未使用的變量)。
小結(jié)
在代碼風(fēng)格規(guī)范的爭論上,每個(gè)人都有自己的理解,永遠(yuǎn)沒有正確的答案。把時(shí)間用于細(xì)枝末節(jié)上爭論,不如多把關(guān)注點(diǎn)聚焦在核心業(yè)務(wù)上。
而不管怎樣爭論,總歸會(huì)選擇一種風(fēng)格。在這個(gè)方面,也需要在個(gè)人語義和普適價(jià)值上做一個(gè)權(quán)衡。
所以,選擇一份前端規(guī)范標(biāo)準(zhǔn)(如 standard),然后保持吧。把時(shí)間留下來解決其他有意義的問題!(^____^)/
參考資料:
-
Javascript的10個(gè)設(shè)計(jì)缺陷 -
eslint -
standard -
prettier -
使用ESLint+Prettier來統(tǒng)一前端代碼風(fēng)格 -
ESLint 在中大型團(tuán)隊(duì)(美團(tuán))的應(yīng)用實(shí)踐 -
Why (and how) to use eslint in your project -
Automate Your Coding Standard
關(guān)于本文
作者:曬兜斯
https://juejin.cn/post/6954150918086475806
