前端組件級別的抽象方向
來自:掘金,作者:阿佛加德奔
鏈接:https://juejin.cn/post/7156575298501214221
前言
基於 2 大主流前端框架下,前端的主要的工作其實就是在編寫各種組件。似乎所有人都在說前端開發的天花板很低,除了一些特別的方向,很少有人在前端功能的開發上遇到過什麼難題。這也導致沒有人關注和討論前端組件的抽象,大家都忙於做功能的開發。甚至有些前端開發在完全不做抽象的情況下僅用少數幾個組件就完成了對整個 SPA 的開發。
爲什麼需要組件級別的模塊抽象?
即使在掘金這樣偏前端的專業社區,也有很多人認爲不需要做模塊抽象。以下是我的上一篇文章《如何把前端項目寫成一座屎山?[1]》下的一個熱門評論:
模塊化抽象的本質仍舊也逃離不了 “高內聚、低耦合” 這 2 個永恆的主題。更具體的,主要是爲了性能和可維護性。但性能問題往往被直接忽略,因爲現代瀏覽器的性能和框架的優化極大抹平了這部分差異。所以拋開一些情況下的性能優化,我們抽象的意義在於提高了代碼的可維護性,帶來了複用、邏輯內聚、穩定的開發效率、較小的心智負擔和 bugfree,所以這絕對是一件具有長期收益的事情。
抽象方向
- 代碼行數
相信大部分前端開發都體會過接手別人代碼,打開一個組件模塊,代碼 1000 + 行那種撲面而來的壓迫感。雖然我也看到過代碼行數控制在小几百行,抽象依舊稀爛的代碼。但暫時沒見過組件超長但抽象仍舊非常好的代碼。代碼行數甚至稱不上作爲衡量抽象好壞的標準,但絕對是最接近本質的一個表象。就我個人代碼風格而言,極少有封裝超過 150 行的組件模塊(代碼風格可參考本人開源項目 KerryCodes/leggo: 一款拖拽式前端表單生成低代碼工具~ (github.com)[2])。
- 視圖頁面結構
這是最樸素的一個組件抽象方向,社區裏討論的也最多,按下不表。
- 單一功能
很多開發總是困惑什麼時候應該開始抽象組件,是因爲這個組件太大了代碼行數太多了,所以需要去做拆分嗎?是因爲視圖結構上相距甚遠所以拆分嗎?不是的,你應該要考慮的是功能。根據 “單一職能” 原則,我們可以基於功能去安排將哪些邏輯抽象成一個獨立的組件模塊。多思考功能級別的拆分,然後把每一個最小原子化功能實現的代碼抽象成一個獨立的組件。
- 複用公共部分
我們難免遇到一些功能相似的組件,導致在多個組件中重寫了一部分相同或相似的邏輯。不要這樣做,儘量把交叉部分的功能做抽象,達到複用的效果。這裏的抽象可以是公共組件也可以是一個公共函數。如果複用和維護性無法權衡怎麼辦?維護性優先!不要爲了複用一點邏輯搞一堆入參或處理大量的邊界條件,導致代碼冗雜難懂,這是不可取的。
- 自動初始化
這是一個非常常見的功能場景。假設你有一個新建彈窗,裏面有一些表單在彈窗開閉的過程中需要恢復初始化值。最常規的開發就是通過手動去恢復初始值。但隨着功能的迭代表單值的增刪變化,我們不得不同時也不斷去維護一大塊邏輯用於初始化,這大大增加了開發的心智負擔,導致維護性性變差。所以,我們的抽象在於使得狀態初始化過程不需要手動去幹預,而交給組件的加載和卸載過程自動去完成。
- 自定義 hooks
相比於 Class 組件,函數組件提供了 hooks,於是我們有了一個非常強大的帶狀態的抽象複用手段。明顯的,我們可以將一些公共的業務邏輯抽象成 hooks 作爲複用手段(當然也更應該首先考慮抽象成一個組件)。另外對於一些非公共的業務邏輯,只被某個特定組件使用,但是卻比較複雜,比較偏過程,個人也會封裝成 hooks 使用。這樣做的好處就在於組件內部會比較乾淨,開發維護的心智負擔下降。
但我們也並非可以濫用自定義 hooks,因爲 hooks 也有一些明顯的缺點。比如副作用,隱式的狀態,邏輯黑盒化等等,這些都增加了心智負擔。(歡迎各位大佬討論這部分內容!)
- 狀態和重渲染
“狀態”可以說是前端組件開發中最重要的一個概念,所有的重渲染都是來自於狀態的改變。很多 bug 也是來自於狀態與視圖不一致的問題。狀態維護的好壞標準在於狀態的改變是否引起的是最小原子化的視圖重渲染。如果一個狀態的改變總是引起與該狀態毫無關係的其它組件重渲染,那麼基本可以確定這一塊代碼的抽象做的很差。這裏可以特別提一下 “純組件” 的優化,出現這種優化技巧的本質就在於狀態改變所導致的不必要高成本重渲染。
有一種非常典型的抽象方式就是 props.children,也可以歸爲這個方向。這個寫法相信很多前端都瞭解,在面試過程中也是一個高頻八股問題。但這種方式不應該被濫用,因爲這會導致代碼視圖結構的清晰度下降。我總結了適用這種抽象的幾個前提:頁面視圖結構導致父子組件無法分離;父子組件關聯度很低;父組件的狀態活躍,而子組件不活躍。本質上在於父組件的狀態變化不引發子組件的不必要重渲染。
- 非受控組件
這個概念來自 React 官方文檔介紹表單組件的部分,我們可以借用一下。如果你的組件總是需要接受來自父組件的傳參,可能就是抽象不好的體現。越多的傳參代表更多的耦合度。
軟件設計有一個非常重要的原則叫 “迪米特原則”,這個原則啓發我們一個模塊應當儘可能少的與其他模塊發生相互作用。儘量使你的組件保持乾淨獨立,狀態在內部完成消化而不是受父組件控制。除了耦合度降低帶來的 bugfree,一個明顯的收益是乾淨的非受控組件在複用時幾乎是沒有心智負擔的。所以需要非常謹慎的使用狀態提升這種手段,如非必要可以儘量避免。
申明:以上觀點來自個人經驗總結和思考,歡迎理性討論和補充。另外所有方式都需要先考慮具體場景,不要無腦使用!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Z5Ltm3maidw42Vels5GnMA