如何编写无法维护的代码_编写可维护的前端代码
大家好,我叫王力國,目前是 RPA 前端團隊負責人,過去一年我們從零構建了 RPA 前端平臺,目前前端維護的代碼行數在 13 萬行左右,其中超過 92% 以上是 TypeScript 代碼,主要有以下三個活躍迭代的代碼倉庫:
在這一段時間里經過大家的努力,應該來說目前看來以上三個倉庫的代碼是比較優雅,維護成本是比較低的(大家應該已經半年沒有加班了,哈哈)。我本人在這段時間里做的主要工作是架構設計和代碼評審,過程積累了一些經驗,非常開心今天能夠有機會同大家一起交流,今天的分享有 10 個部分,分別是:
在正式開始之前,我要跟大家先聲明一下,以上 10 個部分僅是我個人認為優先級比較高的問題,單獨整理出來跟大家一起探討,大家如果想要深究到具體某一行代碼如何編寫的更加優雅的話,可以閱讀市面上更多關于編碼方法論的書,另外,今天這些內容僅適用于上層業務開發的項目,也就是說不適用于開源項目/基礎庫項目。
另外因為我們同時使用了 Angular 和 React,所以今天舉例的時候可能會穿插兩個框架的代碼,不過不會涉及太多框架相關概念,不會影響大家理解。
1/10. 基本約定:
1. 目錄結構
大部分情況下你的目錄結構組織得好,你的可維護性就已經及格了,而一個好的目錄結構應該是可以自解釋的。
這里需要特別解釋一下為什么在 Angular 項目里你最好還可以有一個 modules 目錄,因為大家時間久了會發現,簡單一個 shared 目錄是很難滿足需求的,有時候你僅僅依賴 shared 里的一個組件,卻需要導入整個 shared,這是因為有時候一個路由模塊對應的不僅僅是一個領域,在這種情況下,我非常建議你將 shared 目錄按照領域模型拆分得更細,甚至移除 shared 改為 modules 目錄,可以實現一個路由模塊按需導入幾個領域模塊。
2. 命名風格
需要說明幾點:
2/10. 類型安全
在早期團隊還只有我一個人的時候為了快速開發選擇使用 es6,后來產品被提升為公司戰略級產品,團隊也在爆發式擴大,工程師水平產生了梯度,類型約束的需求越來越大,于是我們非常果斷決定把整個項目改造至 100% TS,當時為了降低遷移成本,代碼仍然充斥著大量 'any',讓人感覺沮喪的是,如果不能用好 TS 的類型系統的話,使用 TS 反倒抬高了心智負擔降低了編碼效率。 后來我們發起了一個學習 TS 的熱潮,大家不斷閱讀文檔,高級教程,學習市面上設計優雅的 TS 代碼,嘗試去建設一個比較標準TS體系。
很快項目里的 any 就在肉眼變少,我們目前已經在全鏈路 lint 檢查中開啟了兩個重要約束:
另外這里要提一點,人的自覺性總是無法被完全信任的,即使你使用了 pre-commit 鉤子來跑 lint,也可以輕易被人繞過,我強烈建議你在云端 ci 流中加入 lint 檢查,且強制約束未通過 lint 檢查的分支無法被合并。
在早期開啟全面禁用 any 之后,我們編碼的效率非常低下,很多時候我們不得不編寫大量的類型定義。
不過好在大部分情況下,我們使用的工具或者庫已經導出了一些工具類型,我們需要自行編寫的工具類型非常少,跟大家推薦幾個資源可以幫助你更快地編寫更安全的 TS 代碼:
3/10. 注釋有罪
這種注釋看起來很搞笑吧?但是我相信你的項目里一定有,而且還在源源不斷產生這種注釋。如何編寫注釋確實是一門藝術,寫太多寫太少都要被人罵。我的建議非常簡單:永遠不要注釋,除非你有充分的理由。
大家想想什么情況下你會抱怨代碼沒有注釋?
那么這就很好理解我們什么時候需要注釋了:
4/10 配置分離
對于組件級別的差異,很多時候 Props 就夠了,如果是多個組件組成的一個模塊需要差異化的話,你可能會使用多級參數透傳或者是靜態變量的方式,不過我想告訴你大多數時候可能 Provider 更合適,尤其是你希望同時在一個應用生命周期里使用幾種配置方案。
其實大家對 Provider 并不陌生,不管是 Angular 或者是 React 實際上都可以輕松使用 Provider ,如下
這里要特別說一下,大家在 Angular項目中編寫復用模塊時,最好養成暴露 InjectToken 的習慣,即使它看起來暫時還不需要配置。
5/10 狀態管理
市面上關于全局狀態管理方案已經比較成熟了,比如 Rxjs,Mobx,Redux... 今天我們不會再過多探討全局狀態管理,更多想要跟大家探討一下局部狀態管理。
首先問大家一個問題:局部狀態管理可以被復用么?或者說應該復用局部狀態管理代碼么?
再問一個問題:如果可以被復用,那么我們應該使用組合還是使用繼承呢?
我的建議是:可以復用,但最好不要使用繼承,可以考慮組合。
實際上在 Angular 里,你只需要簡單的從組件級別注入服務就可以復用局部狀態管理代碼了。是不是好簡單?實際上這就是 MVP 架構的實現,這里的組件級別 Providers 充當的就是 Presenter 層。
而在 16.8 版本 React 開始,你可以使用 Hooks 來組合局部狀態管理,相信大家應該都有聽說過,我們實際用下來的感受是:好用是真的好用,坑也是真的多。
我們在過去使用過程中也沉淀了自己的 Hooks 庫(@bixi/hooks),大家有興趣可以拿去玩玩。
6/10 性能優化
我對性能優化的一貫看法是:最好的時機就是項目立項的時候,其次是現在。
大家最好還是能夠養成編寫高性能代碼的習慣,以下是我們在開發過程中經常會使用的一些性能優化手段,不細說,大家可以自己挨個去研究。
7/10 版本管理
這里版本管理有兩個部分:
8/10 適度封裝
說到封裝,我們先看下面這個代碼段變化過程
我在過去的代碼評審過程中發現很多同學會很喜歡編寫上面這種代碼,他們都能給我一個很好的理由:函數拆得細好復用啊。
可是,你的代碼真的有被復用么?
實際上,在業務代碼開發過程中,很多時候過度封裝反倒會提高復雜度降低可維護性,因此我經常跟大家說的一句話是“你把它搞復雜了”。
我們可以試試用最笨最簡單的方式改造一下上面這個代碼如下
大家會發現,這段代碼行數看起來變多了,但是維護起來變得特別簡單,我在處理 a 邏輯時候我才不關心會不會影響到 b/c/d...
9/10 組件設計
我們來看下面這個組件設計需求,這是我們在應用里的一個真實組件,它有以下四個主要特性:
有些同學的組件拆解原則是:管它三七二十一,先拆到它不能再拆為止。
那么在遇到上面這個組件設計需求的時候它會拆解成下面左邊這樣,甚至粒度更細,那么拆成這樣有什么問題呢?
很明顯,不是組件拆得越細就越好,因此們更推薦你像右圖這樣拆解成兩個組件就夠了(當然如果你的代碼復雜的話,可以再適當拆解)。
我們再看下面這個簡易的 Input 組件,看看它有幾宗罪:
2. 組件不具備復用性,產生了一個searchTasks 事件
3. 組件暴露了實現細節,需要依賴外部的 validate 方法
4. 組件在初始化時拷貝了 value 副本,之后并沒有繼續監聽外部 value 變化刷新副本,導致不是數據驅動的,不是冪等的
10/10 防御式編程
編寫更可靠的代碼當然應該是我們的長期追求,但一些不好的編程習慣可能會讓你的代碼問題變得難以追蹤,你的錯誤監控工具(我們使用 sentry 監控代碼錯誤)可能會變得形同虛設,比如上圖中的代碼:
// bad if (this.editor) {this.editor.destory(); }// bad,等價于上面代碼 this.editor?.destory();之所以說它不好,因為我們知道在這個組件掛載之后,this.editor 是應該必然存在的,那么我們在卸載的時候如果不存在我們應該及時拋出錯誤,而不是悄無聲息吞噬掉,更好的處理方式應該是下面這種方式:
// good,我們斷定它一定存在 (this.ediotor as Editor).destory();總得來說,你最好還是謹慎使用 lodash 或者 optional chaining,該拋錯的地方就讓它及時拋出來。
當然為了不讓你的應用奔潰得太難看,你還是需要做好錯誤收集以及 UI 降級。
謝謝大家,如有錯誤,請不吝賜教,你可以在這里聯系到我。
PPT 可以在這里下載到:
http://cdn.liguo.run/share/clean_code.keyhttp://cdn.liguo.run/share/clean_code.md
總結
以上是生活随笔為你收集整理的如何编写无法维护的代码_编写可维护的前端代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python语法速成方法_30分钟学完P
- 下一篇: 2017年html5行业报告,云适配发布