導語: 隨著業務的增長和開發團隊的成員快速增加,其中很多新人來自於五湖四海各大門派,在編碼的風格和習慣中也出現各異。 通常在相互 codereview 時發現很多程式碼上的問題,久而久之程式碼出現了程式碼難以維護的問題,甚至還會出現低階錯誤。 因此,我嘗試在前端程式碼質量的管控上做了些探索,也總結了一些經驗分享給大家。
作者:鄭振波
本文大綱介紹
- 編碼規範
- 冗餘檔案與程式碼
1. 編碼規範
在一些老專案裡我們常會遇到以下問題:
相信編碼規範對於大家來說不算是陌生,如果在 9102 年再次聊起這個話題,恐怕耳朵會起繭,但編碼規範從制定到落地是一個艱難的旅程,特別是對於不同成員的編碼習慣,還有棘手的祖傳程式碼。無論你是老司機還是新手不妨瞭解一下。1.1 編碼規範的制定
如何制定編碼規範?這是一個永恆的話題,甚至出現過開發者按照自己的習慣和想法不停的去修改 eslint rules,沒錯,主觀性非常強的開發者就會這麼幹,最後發現 eslint rules 成了一鍋粥。
如果客觀些也許會在這三個方面去思考問題:
- 兼顧習慣:儘可能兼顧團隊各成員習慣,人是有個性的,要兼顧似乎不大可能。
- 規則從嚴:規則越嚴格越好,也絕不讓他鬆散無約束。
- 投個票:投票似乎是最民主的決定,但往往最具爭議的 rules 會出現票數差異不大。
如果你能從以上三個方法中取得成果,那說明你是老闆,這一切就會變得太簡單了。但無論如何,每一個被擴充套件的 rule 都能找到具支援點和反駁點,在制定規範時同學們通常往往會在幾個點去發表他的意見:
- 習慣:“我一直都這樣幹,沒問題”
- 業界標準:“你去看某某大公司的開原始碼吧”
- 必要性:“行尾不加分號我從沒見過跑不起來的JS”
- 耗時:“按一次 tab 比按兩次 tab 省時省力;行尾加分號簡直浪費生命”
1.2 在編碼質量和編碼效率之間取得平衡
有沒有辦法可以解決這些爭論?怎樣取得平衡?開源社群各團隊開源出的 eslint rules 可以說是遍地開花,prettier,eslint-config-standard,airbnb 等等,在編碼規範選型時應該考慮以下幾點:
- 適合專案的技術選型:比如我們的團隊技術棧使用了 React 、Node.js,ES6,那麼就可以很清晰地知道怎麼選擇方向了。
- 社群認可度高:相信社群爭論程度不亞於團隊成員,如果一個規範得到 stars 超過 1w,已經是很了不起的事情了。
- 是否有外掛支援:能夠支援 ESLint,JSCS 等。
基於以上標準,我們選擇了超過 8w + 由 airbnb 前端團隊維護的 eslint rules。 標準制定後, 如何讓團隊成員快速適應起來?
- 配置編輯器:如果使用 VSCode,可以配置 "eslint.autoFixOnSave": true,這樣儲存程式碼時會自動根據 eslint rules 去 fix 程式碼,同學們就很舒服了。
- 編輯器外掛:如果使用 VSCode 可以安裝 Eslint 外掛,這樣可以實時提示不規範的程式碼。
- 構建時提示: 如果使用 webpack ,可以使用 eslint-loader,再配合 eslint-friendly-formatter 可以給到開發者很好的提示。可以參考該文章 webpack引入eslint詳解
這樣 eslint 規範就初步落地了。
1.3 整改祖傳程式碼
定製好規範並落地後,如何對於祖傳程式碼怎麼辦?於是我們又遇到不同的聲音:
- 這程式碼不是我寫的:”誰寫誰來 fix 啊“
- 我不敢改: “改出問題要背鍋怎麼說”
- 下次再改:“趕時間釋出呢,要不就下次再改”
- eslint-disable:“disable 大法好,沒有東西可以攔住我了”
以下是我們對祖傳程式碼的整改之路: 首先要看看問題有多嚴重:
npx eslint src
複製程式碼
25W+ Eslint 報錯,這不知道要改到什麼猴年馬月了。
1.3.1 新增 .eslintignore 檔案,排除第三方 js 檔案
但經過排查發現,這裡面大部分報錯是來自於第三方庫,但又不是 npm 包,這些檔案往往是不能滿足當前的編碼規範的,並且有部分是經過程式碼壓縮,更不應該走 eslint 檢查。遮蔽掉第三方檔案的檢測後,剩下的 eslint errors 還有 2w+,錯誤歸類:
可以發現錯誤型別最多的是換行符。1.3.2 關於CRLF
在文字處理中,各作業系統也是有自己的一套標準:
Dos 和 windows 採用“回車+換行,CR/LF”表示下一行; UNIX/Linux 採用“換行符,LF”表示下一行; 蘋果機(MAC OS 系統)則採用“回車符,CR”表示下一行。
所以,如果團隊中還有 windows 玩家那麼建立檔案的換行符就是 CRLF,這也談不上是什麼很大的缺點,但在如果遇上了 Vim 或 Emacs 玩家開啟檔案就會看到這樣的情況:
每行後面會帶上個 ^M。為了維護程式碼世界的和平,通常需要統一轉換為 LF 換行符:- 設定 IDE 換行符,如 VSCode:
- VSCode 預設配置:檔案 - 首選項 - 設定 -搜尋:預設行尾字元。修改為
\n
- EditorConfig: 如果你的專案中有 EditorConfig 那就更好了:
#Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true
- 配置 git config:可以在 git commit 時把程式碼統一轉為 LF,那麼可以瞭解一下
git config core.autocrlf
,也許你會見過這個配置,但不一定正確使用,其實他有3個值可配置:
ture: git pull 時會把 LF 結尾轉為 CRLF false: git pull 時不做任何轉換 input: git pull 時會把 CRLF 轉換為 LF
那麼這裡應該使用的是 git conifg core.autocrlf input
以上任何一條都可以解決你的換行符問題。
1.3.3 自動修復
解決好這一切之後,可以嘗試讓 ESLint 自動修復一波了:
npx eslint src --fix
1.3.4 定期整頓
800+ 的錯誤分佈在各個頁面,可以給團隊成員每人分配幾個頁面修復並分批上線,這樣大規模的修復最重要的是釋出後的監控:
1.4 曾經以為 ESLint 是萬能的
也許你會認為 ESLint 沒有報錯那就 O**K了,其實坑往往沒那麼容易被發現,就像下面這個例子:
這裡提示絕對路徑的引入
應寫在相對路徑引入
的前面,於是快速改他一波:
這裡就有問題了,你看出來了嗎?應該怎麼改?
1.5 守住規則
我們經過制定規範,程式碼整改,接下來需要守住規則,否則一切徒勞
- git hook:可以使用 pre-commit,husky 等工具來配置 git 鉤子,配合上 npm script 可以做到在提交程式碼前執行 eslint 命令來檢測變更檔案是否符合規範,否則 exit 非零值來終止 git commit。但在前端使用 git hook 缺點也是有的,比如每次提交程式碼都要等待 eslint 一遍,導致開發體驗大大下降;也會出現有同學自行刪除 pre-commit hook 檔案來繞過檢測的情況;也可以通過新增
--no-verify
來關閉檢測。 - web hook:好處是使用服務端來做校驗,無法在前端繞過,缺點是需要使用伺服器資源。關於如何搭建 web hook,可以參考這篇文章
1.6 關於前端程式碼規範的其他思考
以上提到的是關於 JS 程式碼規範的內容,對於 CSS 也可以使用 stylelint
來做規範檢測,但這些更多的是對程式碼的格式做規範,如果想把程式碼寫好 ESLint 之類的並不是全能的,比如程式碼的整潔之道,這裡列舉了不少可以參考的正規化,恰又是 ESLint 無法幫你 hold 住的點,所以 ESLint 並不是萬能的。
2.冗餘檔案與程式碼
2.1 關於冗餘檔案
也許你會認為冗餘檔案是一個問題,但他不是一個很關鍵的痛點,如果你的團隊追求極致,有非常的程式碼潔癖,那麼冗餘檔案也應該重視起來。即使你是一名老鳥,也有可能產生冗餘檔案。
2.2 為什麼會產生冗餘檔案
在程式碼的迭代過程中,往往容易忽略刪除相應的檔案,大概有兩種場景: 1、刪除 JS 程式碼中的 require,卻忘記刪除相應的檔案(img/css/js/etc.)。 2、刪除 CSS 中的 background ,忘記刪除相應的圖片檔案。 刪除程式碼很痛快,只要頁面重新整理一波沒有報錯就覺得 o**k 了,日積月累的冗餘檔案慢慢讓整個倉庫越來越大。
2.3 清理冗餘檔案
如果使用 webpack 構建,那就可以很方便地分析出整個專案的依賴關係。 第一步:在根目錄跑命令,把檔案的依賴關係導到 stats.json 中,這個步驟耗時略長,我在專案中跑了一次,1420 個檔案耗時 35s。
webpack --json > ./stats.json
複製程式碼
第二步:使用 glob 獲取目錄下的所有檔案路徑:
glob('!(node_modules)/**/*.*')
複製程式碼
結合第一步生成的 stats.json 可以過濾出未被引用的檔案。
2.4 冗餘程式碼
2.4.1 關於冗餘的 CSS
要分析專案中冗餘的CSS,其實是比較困難的,主要原因有 3 個方面: 1、頁面的元素或元件的巢狀:導致無法只從靜態分析的層面上判斷樣式是否有作用於對應的元素上。 2、樣式的全域性作域:a.css 中宣告一個樣式,他可以作用於頁面上的任何一個 dom,這樣分析起來要遍歷專案中所有的 dom 和 css,一個樣式的宣告需要檢查所有的 dom,如果有N個樣式宣告那將會有非常大的計算量。 3、css 沒有約束: 多個 css 檔案的樣式也可以作用於一個 dom 元素上,因為往往會用到開源的 ui 庫,會根據設計的要求對相應的 dom 元素做樣式覆蓋,所以這種過於靈活的特性帶來的不可控造成難以管理。
2.4.2 解決冗餘的 CSS
CSS Modules 可以把 css 作用域收斂,可以顯式宣告哪個 class 樣式作用於 dom 上,可以保證某個元件的樣式不會影響到其他元件:
樣式與 dom 形成了強依賴關係,這樣就可以便於做靜態分析,可以藉助 eslint-plugin-css-modules: 如果 scss 裡面存在未使用到的 className,會得到提示: 如果在程式碼中使用了未定義的 className,也會有提示:2.5.1 關於冗餘的 JS
在程式碼迭代中,很有可能刪除掉一些方法的使用,那麼會在 Class 留下一些多餘的方法,這些也是難以通過 eslint 來檢測到的,因為沒辦法判斷這些方法在例項化後,是否會在某個時刻被使用到。如果要找出這些冗餘的方法,也需要從整個專案開始分析所有 JS 的依賴關係,那麼計算量就會非常大,並且很耗時。
2.5.2 嘗試解決冗餘的 JS
目前沒有什麼好的辦法來解決這個問題,或許可以通過兩種方式來分析出來: 1、命名約定: 比如以下劃線開頭的方法是私有方法,那麼就可以只針對本檔案所有的私有方法是否被使用來做分析了。 2、註釋標記:如果不喜歡下劃線的方式,那麼也可以考慮新增註釋來標記。
2.6 冗餘檔案/程式碼 小結
1、刪除程式碼要留心 2、分析冗餘 3、合理運用工具 4、釋出後監控