如何保障前端專案的程式碼質量

任俠發表於2019-03-04

對於中大型前端專案,專案規範與程式碼質量尤為重要。當功能需求變更或需要重構時,隨心所欲的(糟糕的)程式碼可能帶來比重新開發還麻煩的問題。

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 制定專案編碼規範

團隊協作專案中,編碼規範尤為重要。對於初級程式設計師,因經驗欠缺,編碼規範的要求可以避免許多低階問題的產生;對於多人團隊來說,風格一致的編碼約定,在協作開發、程式碼移交等時,可以在很大程度上降低風險和成本。

那麼編碼規範應當如何制定?

沒有最好的風格,只有團隊認同的一致性約定。一般來說可以由團隊負責人牽頭制定,成員提意見補充,最後落地成團隊規範並嚴格執行。業界有很多優秀前端團隊開源的規範可供參考。如:

學習編碼規範約定是有必要的,但你能在看完後並真正的理解它們嗎?

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 執行成了批判會。

擴充閱讀:

2.4 編寫單元測試

前端專案寫單元測試,對很多人來說是不願意的,因為編寫過程太過複雜。但基本的單元測試是可以寫的,公共方法和元件的修改可能會為某些呼叫模組製造潛在的 BUG,良好的單元測試可以在出現問題時快速反饋出來。

我們當前對專案單元測試的基本要求是這樣的:

  • 公共方法、服務類必須寫單元測試
  • 公共元件應當書寫單元測試,應儘可能覆蓋到各種功能點
  • 業務元件可以書寫簡單的測試
  • 注意測試程式碼質量,應當反覆通過測試覆蓋率評估和改進測試程式碼

Vue.js/React/Angular 三大框架都有完善的單元測試實現體系,在專案中引入單元測試的成本並不高,高的是測試程式碼編寫的過程。在某些情況下我認為這種“高成本”是值得的。另外需注意,單元測試的執行一定要與 CI 整合,才能真正發揮它實時性反饋問題的作用。

2.5 其他

2.5.1 使用靜態檢查語言 TypeScript / Flow

JavaScript 是一種語法簡單使用靈活的弱資料型別語言,而正是這種過於靈活的特性,使得開發者能夠任性地書寫,但任性是需要成本和代價的。

有時會看到一些後端同學會說,前端看上去也沒那麼難嘛,我只學了一天就開始上手擼程式碼了。他們或許說的沒錯,但是有經驗的前端同學去看一下他們此時產出的程式碼,往往都會表示不忍直視。許多前端開發的同學並非計算機相關專業出身,沒有太多相關的基礎理論知識作為背景,剛開始幹活時在程式碼輸出上“隨心所欲任意妄為”的情況更為普遍。

TypeScript 和 Flow 近兩年來越來越火熱,幾乎當前流行的前端開源產品都在轉向使用它們,這足以體現它們存在的重要價值。靜態檢查語言在編碼階段即可檢測並提示出潛在的型別引用風險,可以在很大程度上避免許多因粗心、誤用帶來的邏輯 bug。良好的型別定義會使得專案模組邏輯結構更為清晰可控。藉助編輯器強大的型別提示功能,程式碼編寫甚至無需看詳細文件即可快速瞭解用法。特別是中大型的複雜專案,選用它們絕對是利大於弊。

2.5.2 構建頁面埋點統計平臺

對於產品質量而言,監控體系是非常重要的一部分。對於前端來說,可以通過設計網站埋點統計平臺來收集頁面資訊,如指令碼報錯、頁面效能卡頓等問題,基於統計資訊回溯分析問題根源,進而進行問題修復和程式碼改進。當然統計資料的作用絕不止這些,這裡不作過多的擴充套件講述。

擴充閱讀:

3 相關參考

關於前端編碼質量,你有哪些經驗體會呢?歡迎來一起探討交流。

相關文章