對於中大型前端專案,專案規範與程式碼質量尤為重要。當功能需求變更或需要重構時,隨心所欲的(糟糕的)程式碼可能帶來比重新開發還麻煩的問題。
1 前端專案程式碼中的常見問題
1.1 凌亂的書寫風格,閱讀體驗差
這個問題不用作過多闡述,想必接手過他人程式碼的同學,多少都有些體會。簡單來說,太過隨意的程式碼會讓強迫症患者難以容忍,難以閱讀理解的程式碼有時甚至不如推倒重來。
1.2 低質量的編碼,bug 不斷
什麼樣的程式碼是低質量或高質量的?好的程式碼可能會讓你如讀小說一般被吸引,糟糕的程式碼會讓你看一眼就不想繼續、甚至看半天而不知所云。
有人可能認為初級程式設計師才會有這種問題,其實不然,一些工作經驗兩三年的同學寫的程式碼依然如此。對於一些個人自學意識不夠積極、沒有團隊規範性指引的同學,很容易習慣成“學習半年、然後重複三年無長進”的情況。
拿出來你可能不太願意相信,下面這些例子即來源於真實專案。你能儘可能地找出其中存在的各種問題嗎?
圖1
圖2
圖3
圖4
圖5
圖6
圖7
以上只是擷取的一些很簡短的列子,那麼涉及大塊複雜邏輯的地方會是怎樣的,試試發揮一下你的想象力。
1.3 功能不分離,邏輯糅合,難以閱讀和理解
這種問題其實是非常普遍的。一個函式幾百行、一個檔案數千行、一個類幾十個方法、方法引數定義隨意、沒有任何註釋、方法與變數命名無明確的語義、資料修改與變更穿插在各種方法中等等。 這樣的編碼方式,你要去理解它的邏輯往往真的很難,一般只能一塊塊一行行的去做閱讀理解(可能還會開啟邊看邊罵娘模式)。
這主要原因在於開發者個人的基礎知識能力、編碼經驗和意識等的不足。
其實針對這種情況,常見的開源的編碼規範都會有所提及。我的建議是這些同學應該好好溫習一下物件導向程式設計、函數語言程式設計、資料結構、常見設計模式,看一看各種開源的編碼規範並嘗試去真正的理解它們。當你回顧一個月前的程式碼時,發現可以改進或重構使得編碼邏輯更為簡潔清晰,說明你是在成長與進步的。
經常看到各種社群中都會有同學問這類問題:新專案正在選型,Vue.js、React、Angular 三大框架哪個合適?其實團隊開發成員對這些都比較有經驗,哪種都可以;如果團隊成員前端開發經驗大都不是太豐富或人員不夠穩定,選擇 Vue.js 最適合,為什麼?因為它更簡單簡潔,容易上手。Vue.js 通過 prop、data、computed、method、watch 等各種鉤子,一定程度上限定了編碼方式與風格,使得初級開發者寫出來的程式碼也不會太難看,這也是它越來越受社群推崇的原因之一。
2 保障前端專案編碼質量的方法
如何保障前端專案的編碼質量呢?依我看來可以從這幾個角度考慮:制定編碼規範、開發工作流 lint 風格強制檢查、定期 Code Review、單元測試。
2.1 制定專案編碼規範
團隊協作專案中,編碼規範尤為重要。對於初級程式設計師,因經驗欠缺,編碼規範的要求可以避免許多低階問題的產生;對於多人團隊來說,風格一致的編碼約定,在協作開發、程式碼移交等時,可以在很大程度上降低風險和成本。
那麼編碼規範應當如何制定?
沒有最好的風格,只有團隊認同的一致性約定。一般來說可以由團隊負責人牽頭制定,成員提意見補充,最後落地成團隊規範並嚴格執行。業界有很多優秀前端團隊開源的規範可供參考。如:
- 參考 Angular 編碼風格指南 | 中文
- 參考 Airbnb JavaScript Style Guide | 中文參考
- 參考 Code Guide by @imweb
- 參考 fex-team/styleguide
- 參考 es6-code-style-guide
- 參考 idiomatic.js – 書寫具備一致風格、通俗易懂 JavaScript 的原則
- 參考 JSDoc 中文文件
- 參考 es6 程式設計風格
- 參考 Vue.js 風格指南
- 參考 Vue.js 元件編碼規範
學習編碼規範約定是有必要的,但你能在看完後並真正的理解它們嗎?
2.2 在開發工作流中配置 lint 風格檢查與修正
在開發工作流中引入工具輔助,可以強制性地實現編碼書寫和提交過程中的 lint 校驗。可以怎麼做?條條大道通羅馬,下面以當前流行的 Git Hook 方案舉例供參考。
2.2.1 開發編輯器及 lint 工具配置
我們在專案中配置 TSLint
外掛以校驗 typeScript
;配置 styleLint
外掛以校驗 CSS/LESS。
我們約定團隊開發均採用 vscode
編輯器,並至少安裝以下外掛輔助開發:
- TSLint
- stylelint
- Document This
- EditorConfig for VS Code
- Prettier – Code formatter
- Debugger for Chrome
2.2.2 新增 .editorconfig 檔案
由於不同開發者可能使用的編輯器不同,但各種編輯器基本都支援 .editorconfig, 故每個專案都應當包含 .editorconfig
,用來統一配置編輯器的換行、縮排儲存格式。
配置參考:
# http://editorconfig.org
root = true
[*]
indent_style = space # 輸入的 tab 都用空格代替
indent_size = 2 # 一個 tab 用 2 個空格代替
# end_of_line = lf # 換行符使用 unix 的換行符
charset = utf-8 # 字元編碼 utf-8
trim_trailing_whitespace = true # 去掉每行末尾的空格
insert_final_newline = true # 每個檔案末尾都加一個空行
[*.md]
trim_trailing_whitespace = false # .md 檔案不去掉每行末尾的空格
複製程式碼
2.2.3 配置 Git Hook 強制執行編碼風格檢測與修正
藉助 Git Hook
,可以在提交程式碼時執行風格檢測與修正,當存在無法通過的內容時,提交會被 block,從而實現編碼規範的強制性執行。
可以利用以下幾個工具來實現這個流程:
husky
它會安裝一系列 git hook 到專案的.git/hook
目錄中,這些鉤子可以檢測package.json
中的scripts
指令碼命令配置,並在程式碼提交時執行它(我們這裡利用pre-commit
鉤子)lint-staged
可以取得所有被提交的檔案並依次執行配置好的任務命令styleLint/TSLint/ESlint
各種 lint 校驗工具,可以配置到lint-staged
的任務中prettier
配置到lint-staged
的任務中,可以實現修正可自動格式化的編碼風格
package.json
中的相關配置資訊參考:
{
"scripts": {
"precommit": "lint-staged",
},
"lint-staged": {
"*.ts": [
"tslint --fix",
"prettier --parser typescript --single-quote --print-width 120 --write",
"git add"
],
"*.less": [
"stylelint --fix",
"prettier --parser less --print-width 120 --write",
"git add"
]
},
"devDependencies": {
"husky": "^0.14.3",
"prettier": "^1.13.5",
"prettier-stylelint": "^0.4.2",
"stylelint-config-standard": "^18.2.0",
"stylelint": "^9.4.0",
"stylelint-config-prettier": "^4.0.0"
}
}
複製程式碼
.prettierrc
配置檔案參考:
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 120,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}
複製程式碼
.stylelintrc
配置配置參考:
{
"extends": [
"stylelint-config-prettier",
"stylelint-config-standard",
"./node_modules/prettier-stylelint/config.js"
],
"rules": {
// 定義一些適合團隊約定的規則
}
}
複製程式碼
通過以上配置,當程式碼提交時,會在 pre-commit
階段執行 .git/hook/precommit
鉤子,該鉤子會查詢並執行 scrpits
中的 precommit
命令,於是 lint-staged
定義的任務會被逐個執行。這套方案也是當前比較流行的做法,在很多開源專案中都有所應用。
擴充閱讀:
2.3 執行 Code Review
編碼規範與 lint 檢查只能讓大家的編碼風格保持一致性,卻無法避免低質量輸出的問題。而這種問題對團隊和產品來說往往卻是致命的。
低質量的程式碼不僅僅只是會製造各種低階 bug,讓測試同學測到沒脾氣,對產品來說,可能很小的需求改動卻需要程式碼有巨大的變動,導致產品迭代週期被潛在地延長。另外,當大家的精力總是聚焦於需求開發和 bug 修復時,產品設計的細節就顧不了那麼多了(別跟我說什麼精益求精,趕時間解決完 bug 就燒香拜佛了),這對產品體驗來說也是很要命的。
什麼樣的程式碼是好的,什麼樣的程式碼是不好的?這來源於知識的學習運用和開發經驗的日積月累。低質量的編碼說到底還是經驗不同、水平存在差異。對於個人來說要通過不斷的學習積累自我提升,對於團隊來說進行 Code Review 評審是有必要的。
那麼 Code Review 應當如何進行?
Code Review 的形式可以多種多樣。如 GitHub 上許多流行專案採用 PR(Pull Request) 工作流的方式,一個 PR 至少經過三人次 review 通過才能合入,這能從流程上較好地保障專案程式碼質量。在有的開發團隊或企業,會引入 gerrit 這種程式碼稽核平臺,過程與此大致相似。但對許多快速迭代的業務產品開發團隊來說,這種需要多人評審通過的模式都不太適合:人力有限、時間緊迫、顧不了那麼多了,於是即使 gerrit 也流於形式,編碼質量只能落到開發人員個人的肩膀上。
相比較而言,定期進行小組討論形式,問題的提出可以得到快速反饋和總結,這會讓大家更有動力一些。
一般來說團隊內有新人入職,基本的 Code Review 是有必要的,這時候編碼規範與風格是 review 的重點。當大家對常見的基本問題都有了比較一致且明確的認識後,探討交流學習則會逐漸成為 Code Review 的主要內容。
對於內部交流氛圍濃厚的團隊,可以鼓勵成員之間互相審閱提交的編碼。對大多數團隊來說,可以這麼來做:主要負責人以抽查瀏覽的形式快速審閱成員提交的程式碼,發現有問題的地方提出並打回改進(問題較多的同學的程式碼應重點關注);團隊定期(可以是每週)以例會討論的形式,對一週提交的程式碼進行抽樣和總結式評審,學習好的編碼方式、探討不好的編碼的理由,甚至進而沉澱出適合團隊的編碼約定。
另外注意一點,Review 過程的操作方式和表達用語非常重要,應當是輕鬆的溝通交流學習的方式,不要把 Code Review 執行成了批判會。
擴充閱讀:
2.4 編寫單元測試
前端專案寫單元測試,對很多人來說是不願意的,因為編寫過程太過複雜。但基本的單元測試是可以寫的,公共方法和元件的修改可能會為某些呼叫模組製造潛在的 BUG,良好的單元測試可以在出現問題時快速反饋出來。
我們當前對專案單元測試的基本要求是這樣的:
- 公共方法、服務類必須寫單元測試
- 公共元件應當書寫單元測試,應儘可能覆蓋到各種功能點
- 業務元件可以書寫簡單的測試
- 注意測試程式碼質量,應當反覆通過測試覆蓋率評估和改進測試程式碼
Vue.js/React/Angular 三大框架都有完善的單元測試實現體系,在專案中引入單元測試的成本並不高,高的是測試程式碼編寫的過程。在某些情況下我認為這種“高成本”是值得的。另外需注意,單元測試的執行一定要與 CI 整合,才能真正發揮它實時性反饋問題的作用。
2.5 其他
2.5.1 使用靜態檢查語言 TypeScript / Flow
JavaScript 是一種語法簡單使用靈活的弱資料型別語言,而正是這種過於靈活的特性,使得開發者能夠任性地書寫,但任性是需要成本和代價的。
有時會看到一些後端同學會說,前端看上去也沒那麼難嘛,我只學了一天就開始上手擼程式碼了。他們或許說的沒錯,但是有經驗的前端同學去看一下他們此時產出的程式碼,往往都會表示不忍直視。許多前端開發的同學並非計算機相關專業出身,沒有太多相關的基礎理論知識作為背景,剛開始幹活時在程式碼輸出上“隨心所欲任意妄為”的情況更為普遍。
TypeScript 和 Flow 近兩年來越來越火熱,幾乎當前流行的前端開源產品都在轉向使用它們,這足以體現它們存在的重要價值。靜態檢查語言在編碼階段即可檢測並提示出潛在的型別引用風險,可以在很大程度上避免許多因粗心、誤用帶來的邏輯 bug。良好的型別定義會使得專案模組邏輯結構更為清晰可控。藉助編輯器強大的型別提示功能,程式碼編寫甚至無需看詳細文件即可快速瞭解用法。特別是中大型的複雜專案,選用它們絕對是利大於弊。
2.5.2 構建頁面埋點統計平臺
對於產品質量而言,監控體系是非常重要的一部分。對於前端來說,可以通過設計網站埋點統計平臺來收集頁面資訊,如指令碼報錯、頁面效能卡頓等問題,基於統計資訊回溯分析問題根源,進而進行問題修復和程式碼改進。當然統計資料的作用絕不止這些,這裡不作過多的擴充套件講述。
擴充閱讀:
- 教你如何打造出一個前端視覺化監控系統
- 基於指令和混合的前端通用埋點方案
- (MZ)資料平臺網站埋點統計實現原理與應用[PPT]
- badjs-report — 前端日誌上報與JS異常監控
- sentry – cross-platform application monitoring, with a focus on error reporting
3 相關參考
關於前端編碼質量,你有哪些經驗體會呢?歡迎來一起探討交流。
- Angular 編碼風格指南 中文
- Airbnb JavaScript Style Guide 中文參考
- Code Guide by @imweb
- fex-team/styleguide
- es6-code-style-guide
- idiomatic.js – 書寫具備一致風格、通俗易懂 JavaScript 的原則
- JSDoc 中文文件
- es6 程式設計風格
- Vue.js 風格指南
- Vue.js 元件編碼規範
- 用 husky 和 lint-staged 構建超溜的程式碼檢查工作流
- 怎樣提升程式碼質量
- 重構 – 程式碼優化技巧
- Code Review 都是怎麼做的?遇到過哪些問題?
- 【譯】如何用人類的方式進行 Code Review
- TypeScript | TypeScript Github
- Flow | Flow Github
- 教你如何打造出一個前端視覺化監控系統
- 基於指令和混合的前端通用埋點方案
- (MZ)資料平臺網站埋點統計實現原理與應用[PPT]
- badjs-report — 前端日誌上報與JS異常監控
- sentry – cross-platform application monitoring, with a focus on error reporting
- lzw.me/a/fed-code-…