對於中大型前端項目,項目規范與代碼質量尤為重要。當功能需求變更或需要重構時,隨心所欲的(糟糕的)代碼可能帶來比重新開發還麻煩的問題。
1 前端項目代碼中的常見問題
1.1 凌亂的書寫風格,閱讀體驗差
這個問題不用作過多闡述,想必接手過他人代碼的同學,多少都有些體會。簡單來說,太過隨意的代碼會讓強迫症患者難以容忍,難以閱讀理解的代碼有時甚至不如推倒重來。
1.2 低質量的編碼,bug 不斷
什么樣的代碼是低質量或高質量的?好的代碼可能會讓你如讀小說一般被吸引,糟糕的代碼會讓你看一眼就不想繼續、甚至看半天而不知所雲。
有人可能認為初級程序員才會有這種問題,其實不然,一些工作經驗兩三年的同學寫的代碼依然如此。對於一些個人自學意識不夠積極、沒有團隊規范性指引的同學,很容易習慣成“學習半年、然后重復三年無長進”的情況。
拿出來你可能不太願意相信,下面這些例子即來源於真實項目。你能盡可能地找出其中存在的各種問題嗎?
以上只是截取的一些很簡短的列子,那么涉及大塊復雜邏輯的地方會是怎樣的,試試發揮一下你的想象力。
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 的換行符 \n
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 執行成了批判會。
拓展閱讀: Code Review 都是怎么做的?遇到過哪些問題?
2.4 編寫單元測試
前端項目寫單元測試,對很多人來說是不願意的,因為編寫過程太過復雜。但基本的單元測試是可以寫的,公共方法和組件的修改可能會為某些調用模塊制造潛在的 BUG,良好的單元測試可以在出現問題時快速反饋出來。 我們當前對項目單元測試的基本要求是這樣的:
公共方法、服務類必須寫單元測試 公共組件應當書寫單元測試,應盡可能覆蓋到各種功能點 業務組件可以書寫簡單的測試 注意測試代碼質量,應當反復通過測試覆蓋率評估和改進測試代碼
Vue.js/React/Angular 三大框架都有完善的單元測試實現體系,在項目中引入單元測試的成本並不高,高的是測試代碼編寫的過程。在某些情況下我認為這種“高成本”是值得的。另外需注意,單元測試的執行一定要與 CI 集成,才能真正發揮它實時性反饋問題的作用。
2.5 其他
2.5.1 使用靜態檢查語言 TypeScript / Flow
JavaScript 是一種語法簡單使用靈活的弱數據類型語言,而正是這種過於靈活的特性,使得開發者能夠任性地書寫,但任性是需要成本和代價的。
有時會看到一些后端同學會說,前端看上去也沒那么難嘛,我只學了一天就開始上手擼代碼了。他們或許說的沒錯,但是有經驗的前端同學去看一下他們此時產出的代碼,往往都會表示不忍直視。許多前端開發的同學並非計算機相關專業出身,沒有太多相關的基礎理論知識作為背景,剛開始干活時在代碼輸出上“隨心所欲任意妄為”的情況更為普遍。
TypeScript 和 Flow 近兩年來越來越火熱,幾乎當前流行的前端開源產品都在轉向使用它們,這足以體現它們存在的重要價值。靜態檢查語言在編碼階段即可檢測並提示出潛在的類型引用風險,可以在很大程度上避免許多因粗心、誤用帶來的邏輯 bug。良好的類型定義會使得項目模塊邏輯結構更為清晰可控。借助編輯器強大的類型提示功能,代碼編寫甚至無需看詳細文檔即可快速了解用法。特別是中大型的復雜項目,選用它們絕對是利大於弊。
2.5.2 構建頁面埋點統計平台
對於產品質量而言,監控體系是非常重要的一部分。對於前端來說,可以通過設計網站埋點統計平台來收集頁面信息,如腳本報錯、頁面性能卡頓等問題,基於統計信息回溯分析問題根源,進而進行問題修復和代碼改進。當然統計數據的作用絕不止這些,這里不作過多的擴展講述。