前端代碼考(算)古(帳)與翻(重)新(構)

不少時候,咱們看見一些不太優雅的代碼、不太整潔的代碼,也很容易能夠推斷出這段代碼是怎麼來的,甚至是能夠推斷出寫這個代碼的人當時的心理狀態和那時候的背景。在前端迅速發展的時代,一份一年多兩年以上的代碼,極可能帶着歷史的色彩地成爲了歷史包袱css

考古🔍

若是某一天,你忽然看見相似下面這些的代碼:html

多個if-return

function f() {
  if (a) {
    return
  }
  if (b) {
    return
  }
  if (c) {
    return
  }
  // ...many codes
}
複製代碼
推測當事人心理:需求要作這個功能,須要加一個條件。好的,就在最前面加一下

函數有不少參數

function f(a, b, c, d, config, isAdmin, isEdit, isAdd) {
    // ...many codes
}
複製代碼
推測當事人心理:後面這個函數功能更復雜了,須要加多一個參數作配置。啊,還要再加一些功能,再多一個參數

很長的相似的判斷

if (a === 1 || a === 2 || a === 3 || a === 4) {
    
}
複製代碼
推測當事人心理:狀態4也要走這個邏輯,那我加多一個&&便可

組件返回值有冗餘的短路表達式

return (
    <> { isXXX && <div>abc</div> } {/* 可能中間有不少其餘代碼 */} { !isXXX && <div>123</div> } </> ) 複製代碼
推測當事人心理:兜底狀況展現123,直接加一下,ok

明顯走不到的邏輯

const data = res[0].some_data || {}
if (!data) {
    return
}
// other code

// =================================
const SOME_FLAG1 = 'xxxx1'
const SOME_FLAG2 = 'xxxx2'
const SOME_FLAG3 = 'xxxx3'

const arr = [SOME_FLAG1]
if (xxx) {
    // arr
}
// 對arr一頓操做
if (!arr) {
    // 怕出錯?加了這個
}
複製代碼
推測當事人心理1:可能arr爲假值,防止報錯
推測當事人心理2:只是改了前面,後面!arr壓根沒看見

常見的代碼片斷莫名其妙的出現

import { func } from 'prop-types';

function a(){
    return 1
}
複製代碼
推測當事人情況:壓根不知道,只是輸入了func按了回車,覺得是function的補全

想要一些常見的工具函數卻代碼提示有多個

結果發現,package.json裏面有lodash,而後本身又寫了一個

又好比想要一個request,發現有3個以上的提示。而後一看,團隊統一的request一個,某我的寫了一個緩存版本又一個,另外一我的又本身寫了一個袖珍版,最後還有一我的寫了一個預處理參數的...前端

推測當事人情況1:壓根不知道有團隊統一的request,本身寫還寫得很挫
推測當事人情況2:知道有request,但不敢改,因此本身封裝多一層,但名字仍是取了同樣的

翻新🔧

開啓eslint/tslint

本人屢次代碼優化重構的經驗,一個沒有lint的項目,開了lint後90%的錯誤均可以經過autofix解決。例如9000個錯誤,跑一下便可變成800多個,能夠修復那些換行、縮進、函數單參數無括號的問題。這些修復不須要測試介入。剩下的那些錯誤須要人工解決node

最多見的須要人工解決的lint錯誤合集:
錯誤 解決方法 緊急程度 風險
下劃線命名 全局搜索,一個我的工修
解構賦值 通常是warning,遇到一個修一個
無狀態class組件 改爲PureComponent或者函數組件
willreciveprops 改爲getderivedstatefromprops
componentwillmount/update 初始state & didmount
== 肯定類型再轉化,最後===
做用域下重複命名 看見就修,但仍是有必要性
ts類型報錯 不影響代碼的執行,但也不能長期無論
html標籤缺乏屬性 如img的alt、button的type,看見就修
promise的rejcet不是error reject(Error(xxx))

中等風險以上的修復,須要自測或者測試,過一遍主流程。不管哪種人工修復,量達到幾百個,都須要測試介入react

精簡if

好比上面的多個if-return、很長的相似的判斷,均可以精簡爲||&&,進一步精簡就是數組操做: [1, 2, 3, 4].includes(a),在另外一篇文章裏面有講到更多的if簡化git

完整的看一下整個文件上下文

上面提到的一些狀況,多是最開始的時候設計是沒什麼問題的,但隨着需求迭代,就不同了。好比多個if-return、明顯走不到的邏輯、重複寫了一些常見的工具函數,這些問題都是由於不完整地看上下文致使的json

舊版react的升級遷移

  • 舊版升級新版的狀況,三個danger的生命週期必須處理掉,其中getderivedstatefromprops風險最大,須要作好各類判斷
  • 簡單的組件嘗試使用hook重寫(不須要特意去,只是需求涉及到的地方順便改)
  • 用了ref、context的class組件重構爲函數組件的時候,記得作好足夠的措施,如forwardRef、useContext
  • 組件必須帶名字,不要export default class extendexport default function() {},不然調試工具不顯示名字
  • 若是不是ts項目,要使用props-types(或者在旁邊寫一個d.ts文件)
  • render函數返回內容、函數組件返回內容不宜太長,必定要作好組件拆分。複雜的js運算,建議抽成組件或者renderxxx函數

重構步驟

本人有屢次歷史大項目重構經歷,常見的case和套路已經在上文提過,接下來是操做步驟的總結數組

無論三七二十一,先lint【動手前】

傳統舊項目,一般沒有lint,須要本身裝。而後找一下團隊如今的lint規範(若是沒有就找業界出名的如airbnb),接着跑一波lint自動修復,此時能夠把縮進換行所有解決,剩下的須要本身手動去修。具體怎麼手動修,前面已經提到了promise

順便修一下改動文件【動手時】

爲何重構?那必然有一個觸發點,或是某個需求,或是發現了不少bug致使沒法正常運行。或是開始有大力維護的計劃。因此先從觸發點開始,在保證功能正常的狀況下,順便把模塊一塊兒重構了,這裏也有幾個要領:緩存

  • 新邏輯必須有註釋,舊邏輯若是沒註釋,但又關聯此次修改的,本身梳理或者找同事來了解一下背景,補全註釋和文檔
  • 無狀態class組件,能夠直接改爲purecomponent。若是是想擁抱hook,那就必須改函數組件。可是有一個前提:全局搜索一下,確認沒有其餘地方使用了extend繼承當前class組件
  • 處理全部的magic number。當你看見if status === 1type === 0這種代碼,這個讓人懵逼的數字就是magic number。此時要看上下文,瞭解這個數字是什麼意思,再使用大寫常量來維護(如const NORMAL = 1;,並加上註釋:// 【xx模塊】狀態正常:1) 。若是多個文件用到,須要提取到更上層
  • 奇葩命名也是能夠在這個環節修復一下。文件內搜索,非對象key、非解構,均可以直接換掉了
  • 不肯定、不敢改、有疑問的地方,使用TODO註釋標記,方便後續回頭解決。如if a == b,從代碼中沒法知道a、b是什麼類型,且業務路徑很長很差復現,先妥協一下,等有時間再改
  • 對於「看不懂」、「不敢改」的函數,你就把它看成一個沙盒就行,能不動的先不要動。時刻記住,保證這段代碼上游輸入不變便可
  • 對於重複的代碼,須要提取出來一個函數,而後引進去調用。沒必要過分封裝,大概就一層很薄的封裝,能解決掉明顯的過多的重複代碼便可
  • 若是發現not defined的lint報錯,但頁面沒問題,那麼只有兩種狀況:那個文件沒用了、那個文件還沒執行到。若是是沒用的,馬上刪掉;若是是沒執行到但不敢動的,能夠屏蔽掉這行eslint報錯並加上TODO,或者直接定義它,讓他執行到的時候也能正常且沒什麼反作用(最好仍是要找到相關同事去問一下)

修改範圍怎麼肯定?

有一個這樣的文件目錄:

- components
  utils.ts
  constant.ts
-- Home
 index.tsx
 Header.tsx
 Footer.tsx
 ...
-- Input
 index.tsx
 index.scss
 ...
複製代碼
  • 若是改的是Header.tsx,Home目錄不大,須要所有一塊兒重構;若是Home目錄很大,那就只改Header.tsx和他的父組件index.tsx
  • 若是Home和Input裏面多了任何常量,致使magic number產生,那麼必須去constant裏面新增定義,並寫好註釋,再引入到組件裏面用
  • 若是Home很大,且多個文件import了一個函數,這個函數已經肯定沒什麼用了或者要抽離,此時須要在Home下全局搜索這個語句,直接刪掉或者抽離

補齊基礎設施【動手後】

功能已經作好了,也順便重構了一波,此時還沒完。考慮到將來繼續維護和重構,因此如今要開始鋪路,方便下次,讓本身和其餘同事更舒服

  • 本身封裝的新的關鍵的函數,須要留下本身的大名、日期,並寫好註釋,甚至能夠把參考的網上的資料連接都貼出來
  • 定時查看以前留下的TODO。不要標了TODO就無論了,定時看看,有時間就去跟進解決掉
  • 將lint-staged加入到git鉤子上,不合格不給提交
  • 公共utils、common、assets文件夾都是公共資源,這些須要根據業務共同點抽取出來,包括工具函數、公共組件、全局配置、定義文件
  • readme補全文檔,包括頁面邏輯、文件目錄組織、開發規範,這些都是你這個時候你來定的了
  • js轉ts項目,部分遷移,也是改到哪裏,哪裏就上ts。暫時沒法上ts的大組件,先給一個d.ts文件作類型聲明也行
  • 全透傳參數,沒法追溯的,只能靠跑業務的時候(若是是服務端的,能夠手動curl一下某個接口)console一下得到字段,並抄一份做爲註釋、文檔。node中間層常常會有,params直接透傳前端給的轉給另外一個服務,response也是從另外一個服務透傳給回前端,或者是{ ...data },經歷過的人天然懂這個痛

總結

雖然平時你們老是自黑,講段子,發自黑、調侃的表情包,吐槽垃圾代碼、歷史代碼、知道沒用但不敢動.jpg。但我相信若是真的擱在頭上了,你們都不會慫的,也不會輕易妥協,會認真的修好,完美完成重構

關注公衆號《不同的前端》,以不同的視角學習前端,快速成長,一塊兒把玩最新的技術、探索各類黑科技

相關文章
相關標籤/搜索