eslint+husky+prettier+lint-staged提升前端應用質量
目前在梳理前端應用時發現很多程式碼不規範的地方,包括簡單的js問題以及程式碼格式化的問題,造成了程式碼可讀性下降,另外各種歷史程式碼也是“風格迥異”,甚至影響了應用質量。應用開發成員大部分由於之前是開發後端,對前端開發經驗不足以及許多前端知識體系都是在開發過程中現學現用慢慢積累的,另外,痛定思痛總在想對於前端應用程式碼質量是否也存在諸如Java開發規約掃描外掛,類似的前端程式碼質量掃描外掛進行把控。經過查閱資料,採用 eslint+husky+prettier+lint-staged
方式來對前端質量進行一定程度上的提升,之所以採用這樣的方式,針對的痛點如下:
- 程式碼規範落地難 :程式碼規約相當於團隊乃至公司的整個技術團隊協作的契約,同時這些規範是經過許許多多前輩大師經過專案的洗禮留下的寶貴財富可以在開發中少走很多的彎路,但是,面對開發規範經常面臨的現狀是很難落地,總是“拆東牆補西牆”,歸根結底在於需要工具去強行保證程式碼必須經過程式碼開發規範的掃描;
- 低質量程式碼帶入線上應用 :在實際開發現狀中開發人員可以很簡單的執行git push操作將原生代碼帶入遠端分支上,如果程式碼質量低下就很容易對線上應用質量埋下隱患,至於在合併的時候在進行CR,這樣反饋鏈路太長,最好的方式本地進行commit的時候,最起碼需要保證當前程式碼能夠滿足團隊制定的開發規範,如果不通過,commit都無法成功,這樣能夠從最源頭保證程式碼質量問題;
- 程式碼格式難統一 :按照現狀團隊的開發成員對程式碼的格式都有很大的不同,甚至有些人字串喜歡用雙引號,有的人喜歡使用單引號等等問題,如果程式碼格式化問題不統一,總覺得互相檢視其它人的程式碼總覺得怪怪的,甚至程式碼格式錯亂,嚴重影響了程式碼可讀性,無疑也增加了團隊內的溝通成本,針對這樣的情況,需要一種工具能夠保證團隊內程式碼的格式是一致;
- 程式碼質量文化難落地 :通過引入程式碼質量工具,在開發過程中能夠時刻對自身程式碼質量進行約束,逐漸培養自身對程式碼質量有“潔癖”的開發觀念,同時也會成為團隊乃至自身對質量文化落地的一個抓手。
針對以上痛點,採用 eslint+husky+prettier+lint-staged
這幾個工具能夠有效解決上述問題,其中執行流程和原理在下面給出( 同時關於對程式碼質量文化另一個抓手—CR也可見於這篇文章 )。
1.2 達成效果
要想防患於未然,防止將存在潛在問題的程式碼帶到線上環境,最好的辦法是在本地提交程式碼時就能夠掃描出潛在的錯誤,並強制將其修改後才能提交,這樣就不會將問題程式碼攜帶到線上,就能保證線上程式碼至少不會存在低階的程式錯誤。針對這樣的訴求,可以採用 husky、lint-staged、eslint以及prettier
外掛來實現,具體效果如下:

如上圖,當試圖commit程式碼時,由於掃描出錯誤就不能進行提交成功,必須將其修改成功方可commit。
1.3 配置檔案
在前端應用中的 package.json
中新增如下檔案:
{ "scripts": { "precommit": "lint-staged" }, "lint-staged": { "src/**/*.js": [ "eslint --fix --ext .js", "prettier --write", "git add" ] }, "devDependencies": { "eslint": "^5.0.0", "eslint-config-ali": "^2.0.1", "eslint-plugin-import": "^2.6.0", "eslint-plugin-react": "^7.1.0", "husky": "^0.14.2", "babel-eslint": "^8.1.1", "lint-staged": "^4.0.0", "prettier":"^1.16.4", "eslint-plugin-prettier":"^3.0.1", "eslint-config-prettier":"^4.0.0" }, } 複製程式碼
增加 .eslintrc.js
掃描規則:
module.exports = { "extends": ["eslint-config-ali","prettier", "plugin:prettier/recommended"], "parser": "babel-eslint", "rules": { "prettier/prettier": "error", "strict": "off", "no-console": "off", "import/no-dynamic-require": "off", "global-require": "off", "require-yield": "off", }, "plugins": ["prettier"], "globals": { "React": "readable" } }; 複製程式碼
增加 .prettierrc.js
檔案,用於在掃描通過後格式化程式碼(該步驟可選,如果不引入prettier的話,相應的在package和eslintrc中去除掉相應配置即可)
module.exports = { printWidth: 80, semi: true, singleQuote: true, trailingComma: 'none', bracketSpacing: true, jsxBracketSameLine: false, arrowParens: 'avoid', requirePragma: false, proseWrap: 'preserve' }; 複製程式碼
在前端工程中引入以上的配置檔案即可,這樣就可以達到1.3中的效果。置於其中的實現原理在下面進行分析,有興趣的可以繼續往下看
2. 實現原理
2.1 執行流程
達到上述效果,執行的流程如下:
- 待提交的程式碼git add 新增到暫存區;
- 執行 git commit;
- husky註冊在git pre-commit的鉤子函式被呼叫,執行lint-staged;
- lint-staged 取得所有被提交的檔案依次執行寫好的任務(ESLint 和 Prettier);
- 如果有錯誤(沒通過ESlint檢查)則停止任務,同時列印錯誤資訊,等待修復後再執行commit;
- 成功commit,可push到遠端
在上述流程中,有這樣幾個核心點:
- husky註冊git的鉤子函式保證在git 執行commit時呼叫程式碼掃描的動作;
- eslint完成按照配置的規則進行掃描;
- Lint-staged保證只對當前add到git stage區的檔案進行掃描操作,這樣做的原因在於, 如果對全工程的檔案進行掃描的話,並且之前的前端工程並未注重程式碼規則的檢測的話,很大可能性會出現成百上千的error ,基本上心裡是崩潰的。因此,只對當前add的檔案進行檢測,達到及時止損的目的,歷史程式碼可以切到新的分支進行修復後再進行合併。
2.2 外掛說明
在2.1中的執行流程中主要使用到eslint,lint-staged等外掛,下面分別對這幾個外掛進行說明。
2.2.1 eslint
lint是什麼?
lint是對前端程式碼按照程式碼規則進行靜態掃描的工具,主要負責對當前原生代碼進行規範檢查,如果不符合當前制定的規範則lint則會報出error,並且和lint-staged結合使用就會保證未通過程式碼規範的檢查不能被提交到遠端分支上,這樣就能保證線上程式碼的質量。
為什麼要引入eslint?
引入eslint在我的思考中可以具備如下的優點:
- 既然是程式碼掃描的工具,自然可以保證開發規約的落地,諸如java開發規約的掃描外掛,對於前端而言eslint就是這樣的一個工具,通過配置程式碼規範來確保指定的程式碼規約進行落地。常見的一些大廠的程式碼規範有阿里的前端規範,airbnb,鵝廠alloyteam團隊等;
- 確保應用的線上質量,這點無須贅述,結合其他工具的使用,不滿足程式碼規範的程式碼都不能push到遠端分支上去;
- 更高的可讀性,eslint會對程式碼質量進行掃描,並且一般結合prettier使用的話,在通過程式碼規範後可以對程式碼進行格式化,可以保證程式碼可讀性;
- 避免低階的錯誤,通過
eslint --fix
可以根據規範對程式碼的部分低階問題進行更正。
怎樣使用eslint?
按照文章開頭給出的 package.json
進行配置即可,關於lint的使用可以看官方文件,eslint配置的程式碼規範也在文章給出的 eslintrc.js
中給出,所謂程式碼規範自然而然是根據團隊現狀指定的,我們當前採用的是阿里前端規範 eslint-config-ali
,當然也可以按根據現狀”因地制宜“的去制定。另外需要指出的是,在 eslintrc.js
配置檔案中配置了 globals
變數:
"globals": { "React": "readable" } 複製程式碼
這是因為前端使用的React框架,eslint會對React等變數進行掃描會報出 no-undefined
的error,需要另其為global變數才能避開掃描。在eslint的rules的設計中是可配置化的,在 rules
的屬性中可以設定,具體eslint的rule可見官方文件,其中打勾的是 eslint:recommended
推薦使用的規則。
2.2.2 husky
試想如果將程式碼已經push到遠端後,再進行掃描發現多了一個分號然後被打回修改後才能釋出,這樣是不是很崩潰,最好的方式自然是 確保本地的程式碼已經通過檢查才能push到遠端,這樣才能從一定程度上確保應用的線上質量 ,同時也能夠避免lint的反饋流程過長的問題。
那麼什麼時候開始進行掃描檢查呢?這個時機自然而然是本地進行 git commit
的時候,如果能在本地執行git commit操作時能夠觸發對程式碼檢查就是最好的一種方式。這裡就需要使用的git hook。
什麼是git的hook
git的hook可以理解成當執行如 git add、git commit
等git操作時的回撥,可以檢視 .git
檔案下的hooks目錄,這裡存放的是git相關操作的一些指令碼例子。通過git hook就可以在本地進行commit的時候觸發程式碼掃描來確保原生代碼的質量。關於git hook可以看這篇文章。
2.2.3 lint-staged
在使用eslint和husky能夠保證程式碼的質量問題,但是在實際工程中卻還必須面臨一個問題。現實情況下,一個應用一般是多個開發參與,並且在應用的生命週期中還涉及到人員變更,這就意味著一個應用是被來自”五湖四海“人共同維護,難不免有”廣式燒臘、天津狗不理“各種口味,更難免的還有”臭味相投“的。
針對這些歷史程式碼時,如果提交程式碼時,對其他未修改檔案都進行檢查,一下出現成百上千個錯誤,估計會嚇得立馬刪掉管理eslint的配置,冒出一身冷汗。如下圖所示(來源於網路)

修改了A檔案,B、C、D等檔案的錯全部都冒出來了。針對這樣的痛點問題, 就是每次只對當前修改後的檔案進行掃描,即進行git add加入到stage區的檔案進行掃描即可,完成對增量程式碼進行檢查 。如何實現呢?這裡就需要使用到 lint-staged
工具來識別被加入到stage區檔案。關於lint-stage可以 看官方文件 ,現在會過頭來看 package.json
檔案就能夠理解其中的意思:
"scripts": { "precommit": "lint-staged" }, "lint-staged": { "src/**/*.js": [ "eslint --fix --ext .js", "prettier --write", "git add" ] }, 複製程式碼
在進行git commit的時候回觸發到git hook進而執行precommit,而precommit指令碼引用了lint-staged配置表明只對git add到stage區的檔案進行掃描,具體lint-staged做了三件事情:1. 執行eslint --fix操作,進行掃描,若發現工具可修復的問題進行fix;2. 執行prettier指令碼,這是對程式碼進行格式化的,在下面具體來說;3. 上述兩項任務完成後對程式碼重新add。
2.2.4 prettier
Prettier工具主要用來統一程式碼格式的,eslint也會對程式碼進行一定程度的格式校驗,但主要是用來對程式碼規範的掃描,而prettier則是專門用來對程式碼進行格式化,兩個工具各司其職,為程式碼質量進行保駕護航。它的主要原理是 將格式化前的程式碼和格式化後的程式碼進行比對,如果發現不一樣,prettier就會對其進行標記並按照指定的格式化規範進行修復 。下面的圖可以看出prettier的能力(配套來源於網路)


可以看出prettier能夠對程式碼進行格式化,這樣就可以保證程式碼的可讀性。perttier能夠支援多種格式如JSX、HTML等等,具體可檢視官方文件,對程式碼格式化標準可以根據團隊現狀共同來決定。
與eslint配合使用的外掛
為了和eslint配合使用需要引入兩個外掛 eslint-plugin-prettier和eslint-config-prettier
,其中需要說明的是 eslint-config-prettier
外掛的作用,這個外掛是如果eslint的規則和prettier的規則發生衝突的時候(主要是不必要的衝突),例如eslint 限制了必須單引號,prettier也限制了必須單引號,那麼如果用 eslint 驅動 prettier 來做程式碼檢查的話,就會提示2種報錯,雖然他們都指向同一種程式碼錯誤,這個時候就會由這個外掛來關閉掉額外的報錯。關於 eslint-config-prettier
可以看它的 官方文件 。
3. IDE外掛配套使用
在實際開發中可以在IDE中安裝適配的外掛來提升開發效率,安裝開發外掛後在編碼的時候如果存在不符合規範的地方會有紅色的波浪線提示,並且有一鍵fix的功能,這樣能夠提升效率。下面以webstorm舉例
eslint的外掛安裝方式

如圖,在webstorm中只需要指定eslint的配置檔案即可。
prettier外掛的安裝方式

如圖,webstorm已經內建了prettier功能,只需要保證當前工程已經安裝了prettier package即可。