編寫高質量可維護的代碼:組件的抽象與粒度

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲一名精緻的前端豬豬女孩,也有那麼點想讓自己的代碼同樣看起來精緻一點。所以在拿到新需求的 UI 設計稿時,經常會面臨如下問題:如何拆解頁面?如何劃分組件纔算是合理?好像用於組件拆分的 A 方案和 B 方案在當前業務場景下也都還算合理,那究竟要怎麼選擇?組件的抽象與粒度貌似是一個老生常談的問題了~學習了很多前輩的文章,那麼今天結合業務場景,也來講下我的心得~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"React 官方文檔"},{"type":"text","text":" (https:\/\/react.docschina.org\/docs\/components-and-props.html)上說:組件,從概念上類似於 JavaScript 函數。它接受任意的入參(即 “props”),並返回用於描述頁面展示內容的 React 元素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Vue 官方文檔"},{"type":"text","text":" (https:\/\/cn.vuejs.org\/v2\/guide\/components.html)說:組件是可複用的 Vue 實例,且帶有一個名字。我們可以在一個通過 "},{"type":"codeinline","content":[{"type":"text","text":"new Vue"}]},{"type":"text","text":" 創建的 Vue 根實例中,把這個組件作爲自定義元素來使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實總的來說,無論什麼語言框架,組件就是一段代碼片段,它可以實現某些指定的功能或渲染特定的展示效果,我們一般可以通過 "},{"type":"codeinline","content":[{"type":"text","text":"import"}]},{"type":"text","text":" 的方式將其引入到項目代碼中。本文接下來將以 React 爲例進行相關描述。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"組件的抽象"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"組件抽象的過程就是將通用性代碼“提取”或是“抽取”出去的過程,那麼問題來了,我們爲什麼要抽組件呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"爲什麼要抽組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說到爲什麼要抽取組件,不知道各位讀者有沒有遇到過一個 js 文件中有 1k+ ~ 2k+ 行 React 代碼,甚至更多行代碼的情況。這種情況往往導致代碼難以維護,當有新的需求涉及相關改動時,在一定程度上增加了代碼的學習成本(特別是當你剛剛新接手了一份完全不熟悉的項目的時候)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其次,某些情況下,有一部分代碼在不同場景下其實是可以複用的,例如新增和編輯的彈窗,可能只有彈窗的標題和某些字段有部分差異,此時沒必要把高度相似的代碼複製兩遍,增加代碼的冗餘。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/df\/df3c045b385b86cef3aa4d0b6852e679.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,在我們日常開發中,組件抽取是有必要的,其目的在於代碼的"},{"type":"text","marks":[{"type":"strong"}],"text":"分層"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"strong"}],"text":"複用"},{"type":"text","text":",降低項目的複雜度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"組件抽象的基本原則"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"單一性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單一性要求一個組件具有高內聚,低耦合的特徵,它只負責一件事情,不要耦合一些沒必要的邏輯,並且儘量不要和其他組件有過於多的雙向交互和互相依賴關係。單一性並不代表着不可以引用其他組件,當前組件可能是外層的容器組件,裏面包含一些子組件,這樣的設計是沒問題的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"複用性\/通用性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在設計組件的時候,一定要考慮組件的複用性或者說是通用性。這是指,當組件封裝好後,可以在"},{"type":"text","marks":[{"type":"strong"}],"text":"類似"},{"type":"text","text":"的使用場景中直接調用。這要求我們在設計組件的時候,考慮組件功能的通用性,以及考慮組件入參的合理性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此時有兩種情況:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一種是很多不同的項目間,可能存在類似的使用場景,因此會提煉出一個公共的組件,爲了複用。一般我們稱之爲基礎組件或業務組件,姑且叫它公共組件吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一種是在項目內部,僅在當前場景下作爲一個獨立的模塊可以抽取出來作爲一個組件,暫時稱之爲項目組件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公共組件和項目組件在設計上的側重也有所不同,公共組件要更多的考慮通用性,通過一個組件滿足不同項目中相似的使用場景,比如 AntD 基礎組件庫。而項目組件更多的是處理當前業務中的特殊場景,可能是頁面拆解後的不同模塊,也可能是不同操作的彈窗,往往這種組件不適合直接“移植”到其他項目中使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然鵝,對於一個組件來說,個人認爲也不能一味的追求通用性使其變得難以維護。例如,當遇到下述頁面的時候,要如何抽象組件呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/90\/90fd1e75e9d362088b2c6f0ed826458f.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不難發現,頁面中交易方式、基礎配置和合同設置這三個模塊其實是具有一定共性的,全部呈現爲列表形式,只是在某些列上有展示差異。前輩的做法是,考慮了所有情況,抽象成一個組件。通過 title 區分模塊名稱,由於僅在交易方式模塊有操作列,因此通過 areaCode 區分當前頁面下的不同模塊等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在業務發展前期,這樣抽取的組件的確使用起來很方便,且通用性很強。但隨着業務的膨脹,同一項目中不同頁面開始出現相類似的模塊,於是新增 pageId 標識,用於區分不同的頁面以及對應頁面的特殊邏輯。又過了一段時間,新增 userIdentity 標識,用於控制不同登錄用戶對頁面的查看或操作權限。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"長此以往,新增的參數越來越多,組件內部開始出現大量的判斷邏輯,儘管這個組件通用性很好,能應對各種頁面展示邏輯,但這也使它本身變得逐漸難以維護。還有一種比較好的解決方案是通過表單中心生成一份這樣的頁面,可參考本團隊之前的一篇文章《"},{"type":"link","attrs":{"href":"http:\/\/mp.weixin.qq.com\/s?__biz=MzI0NTE5NzYyMw==&mid=2247484415&idx=1&sn=0582b22be66659b8431bc1a1da4cb084&chksm=e9537da7de24f4b18b703baf47d73ba9965992730ed65ace82b9a45391f7d47c99d6e041d3d1&scene=21#wechat_redirect","title":null,"type":null},"content":[{"type":"text","text":"動態化表單設計"}]},{"type":"text","text":"》。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"分離處理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"師父曾教導我說抽組件最好做一下業務層和視圖層的分離處理,其中視圖層主要負責頁面展示樣式和交互,業務層主要負責處理業務邏輯,比如接口調用,數據結構調整等。這樣做的好處除了職責分離,還可以有效提高組件性能(比如視圖層可以用 PureComponent 處理)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,例如上述的新增和編輯彈窗,當新增和編輯兩個操作需要分別調用不同接口時,業務層和視圖層的分離處理可以避免組件中耦合對“新增”或“編輯”的判斷,它們可以共用一個視圖,並在各自的業務層實現不同的業務邏輯。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"組件分類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"業務組件 vs UI 組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"業務組件側重於數據和業務的邏輯處理,其中數據一般通過接口獲取。目前本團隊維護的業務組件庫,可以使開發人員即來即用,組件內部有完善的功能和接口數據處理,將組件引入到項目後可直接實現對應功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"UI 組件一般也可以稱爲基礎組件,它們經常在多個地方被複用,且不耦合任何的業務功能,例如:AntD 組件庫。UI 組件側重於頁面展示效果,大部分 UI 組件具有原子性,一些複雜的 UI 組件可以由基本的 UI 組件構成。一般情況下組件內部的數據來源於父組件傳遞過來的 props。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"純組件 vs 非純組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有一天,我看到前輩大神這麼寫的代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"export default class NotFound extends PureComponent {\n  \/\/ 此處省略具體代碼\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是去學習了下純組件和非純組件的區別,首先讓我們瞭解下 "},{"type":"text","marks":[{"type":"strong"}],"text":"React 中的各種組件"},{"type":"text","text":" (https:\/\/zhuanlan.zhihu.com\/p\/30659051)一文中對 React 組件重新渲染機制的描述:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般當一個組件的 props (屬性)或者 state (狀態)發生改變的時候,也就是父組件傳遞進來的 props 發生變化或者使用 "},{"type":"codeinline","content":[{"type":"text","text":"this.setState"}]},{"type":"text","text":" 函數時,組件都會進行重新渲染。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而在接受到新的 props 或者 state 到組件更新之間,其實會執行生命週期中的一個方法 "},{"type":"codeinline","content":[{"type":"text","text":"shouldComponentUpdate"}]},{"type":"text","text":",當該方法返回 "},{"type":"codeinline","content":[{"type":"text","text":"true"}]},{"type":"text","text":" 時纔會進行重渲染,如果返回 "},{"type":"codeinline","content":[{"type":"text","text":"false"}]},{"type":"text","text":" 則不會進行重渲染。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"純組件和非純組件的區別在於,一般情況下非純組件並未自動實現 shouldComponentUpdate 方法的功能(但可以手動調用這個鉤子),而純組件中利用 "},{"type":"codeinline","content":[{"type":"text","text":"shallowEqual"}]},{"type":"text","text":" 的方法對 props 和 state 做淺比較實現了該功能。實際應用中,純組件一般用於純展示型組件,相對於非純組件來說,減少了手動判斷 props 或者 state 變化的繁瑣操作。並且,純組件可以通過減少 "},{"type":"codeinline","content":[{"type":"text","text":"render"}]},{"type":"text","text":" 調用次數來降低性能損耗,但是使用過程中也一定要確保此類組件的渲染僅取決於 props 與 state。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非純組件的話,其實我們日常開發中比較常用。一般情況下,在不做特殊處理時,正常 "},{"type":"codeinline","content":[{"type":"text","text":"extends Component"}]},{"type":"text","text":" 出來的組件都可以認爲是非純組件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"export default class MyComponent extends Component {}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以根據實際的開發場景選擇繼承自 "},{"type":"codeinline","content":[{"type":"text","text":"PureComponent"}]},{"type":"text","text":" 還是 "},{"type":"codeinline","content":[{"type":"text","text":"Component"}]},{"type":"text","text":"。值得注意的是,由於純組件中做的是淺比較,因此帶有深層嵌套的數據是對比不出來的,請慎用~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"組件的粒度"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提到組件的粒度,大多數人的第一反應可能認爲拆分的越細越好。但是,這樣一定是最優解嘛?個人認爲其實不是的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"組件拆解的過於細緻可能導致某些參數從父組件開始一層層向子組件傳遞,容易漏傳,錯傳,或者其中某層組件忘記判空的時候,可能會導致頁面報錯。雖然可以通過 React Context 去獲取,不過好像還是“徒手傳遞”的人更多一點。但組件如果拆解的太粗略往往也會導致複用率低、難以維護等問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"講到這裏,讓我想到了 "},{"type":"text","marks":[{"type":"strong"}],"text":"原子設計"},{"type":"text","text":" (https:\/\/atomicdesign.bradfrost.com\/)。原子設計是 Brad Forst 於 2013 年提出的設計概念,該作者用 5 個層級來描述組件庫的設計。做下類比,映射到開發人員使用和熟知的組件中,個人認爲也適合描述組件粒度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/87\/873ed08f0b47a33360f219ed42b6ad36.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原子組件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果說,原子是物質的基本組成部分,那麼原子組件就可以作爲構成我們所有頁面的最基本組成部分。原子組件,可以爲上文中提到的基礎 UI 組件,例如一個 Input 或一個 Button。它們往往具有不可再拆分的特性,是其他組件的基礎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"分子組件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分子組件一般由幾個簡單的原子組件組成,比如由一個 Label 和一個 Input 組成的姓名輸入組件。這種粒度的組件初步具有一定形態和自身屬性,與原子組件相比,有一定的可操作性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d3\/d361bf3e0cfd702a71b3819994b0ffa2.png","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"生物組件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生物組件是由原子組件及分子組件組成的相對複雜的構成物,它是一種作爲一個單元發揮作用的集合體。比如由姓名輸入組件和一組按鈕組成的搜索組件。在這個組件中,姓名輸入組件被放置在一種使用環境中,實現了簡單的功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/92\/920a777e6d9908e17e28e5af4e7eaa39.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有些生物組件是由不同的分子組件構成,但也有可能由相同的分子組件構成,比如網站首頁的商品展示組件,該組件由六宮格組成,每個格子使用同一個分子組件進行渲染和展示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/72\/7269e0c8817f006d22b1ff6aefb2f98f.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"模板組件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模板組件是由原子、分子、生物組件按照一定佈局結構組成的區塊。它們專注於頁面的基礎內容結構,而不是頁面的最終內容。模板組件是更復雜一點的生物組件,更多的賦能於功能和展示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/05\/05ddf352b90a2e33a51717e1fa1014fa.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"頁面"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終,通過不同模板組件的拼裝,可以生成一個完整的頁面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在實際應用中,組件設計時的粒度往往也需要依據具體的場景具體分析,但原則可以參考高內聚,低耦合的思路,使自己的組件易於維護,同時使自己的整個項目代碼看起來乾淨利落。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實,本人真心認爲組件的抽象與抽象粒度這件事,沒有一個一成不變的統一標準,也沒有對與錯。在基本原則不變的情況下,更多的應該去關注如何適配不同的業務場景和需求要求,求的是“適合”。有時,同樣的場景,組件粒度的標準也會隨業務場景變化而變化,甚至可能隨場景而持續重構。不過爲了代碼更好的維護和分層,以及避免代碼邏輯的過度疊加和膨脹,團隊中可以制定一些組件抽象的規範稍稍加以約束。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"頭圖:Unsplash"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:魚魚"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:https:\/\/mp.weixin.qq.com\/s\/6U8zMpnBk9nBI_bQAobdfw"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:編寫高質量可維護的代碼:組件的抽象與粒度"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來源:政採雲前端團隊 - 微信公衆號 [ID:Zoo-Team]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"轉載:著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章