前端組件設計之一——設計原則

前言:
    前端面試中,請你設計一個通用的Select組件,要求手擼代碼或說一下設計思想?
    我只會寫經常使用的業務組件,最多再把數據抽象一下,其餘的就不知如何下手了。
    今天的文章咱們就先了解一下組件的設計原則,懂理論才能更好的實踐。
複製代碼

組件設計的基本原則

一個組件的複雜度,主要來源就是自身的狀態;即組件自身須要維護多少個不依賴於外部輸入的狀態。html

組件開發中,如何將數據和UI解耦,是最重要的工做。前端

組件開發過程當中,時刻謹記、思考是否符合如下的原則,能夠幫助你開發一個更完善的通用組件。react

單一職責

你的組件是否符合只實現一個職責,而且只有一個改變狀態的理由面試

如fetch請求和渲染邏輯,應該分離。由於fetch請求時會形成組件從新渲染,渲染時的樣式或數據格式變化,也會引發組件從新渲染。數組

單一職責能夠保證組件是最細的粒度,且有利於複用。但太細的粒度有時又會形成組件的碎片化。網絡

所以單一職責組件要創建在可複用的基礎上,對於不可複用的單一職責組件,咱們僅僅做爲獨立組件的內部組件便可。數據結構

通用性

組件開發要服務於業務,爲了更好的複用,又要從業務中抽離。架構

下面代碼實現了需求A:實現一個基礎的select組件: menu:是select的下拉列表,menu上面的div是select的選擇框頭部,包含一個值和一個箭頭。ide

<div className={dropdownClass}>
  <div className={`${baseClassName}-control ${disabledClass}`} onMouseDown={this.handleMouseDown.bind(this)} onTouchEnd={this.handleMouseDown.bind(this)} >
    {value}
    <span className={`${baseClassName}-arrow`} />
  </div>
  {menu}
</div>
複製代碼

此時又有一個新的需求B,要求將select選擇框頭部渲染爲一個圖片。函數

雖然B的交互模式和 A如出一轍,但由於兩者在 DOM 結構上的巨大差異,致使咱們沒法複用上面的這個 Select 來實現它。 只能去修改源代碼、或從新寫一個符合需求的組件。

所以組件開發時最好的作法是放棄對DOM的掌控,只提供最基礎的DOM、交互邏輯,將DOM的結構轉移給開發者。

下面的代碼是Antd的組件DropDown,能夠看到只有最基礎的DOM,提供了多個渲染函數和處理邏輯。

return (
  <Trigger {...otherProps} prefixCls={prefixCls} ref="trigger" popupClassName={overlayClassName} popupStyle={overlayStyle} builtinPlacements={placements} action={trigger} showAction={showAction} hideAction={hideAction} popupPlacement={placement} popupAlign={align} popupTransitionName={transitionName} popupAnimation={animation} popupVisible={this.state.visible} afterPopupVisibleChange={this.afterVisibleChange} popup={this.getMenuElement()} onPopupVisibleChange={this.onVisibleChange} getPopupContainer={getPopupContainer} > {children} </Trigger>
);
複製代碼
  • 複用一個組件時,即複用其職責,因此只有單一職責的組件,是最便於複用的
  • 當一個組件錯誤地有多個職責時,就會增長複用時的開銷。
  • 儘可能避免代碼重複,重複兩次及以上的代碼,考慮一下是否能夠複用?

通用性雖好,但會浪費開發者不少精力,所以在抽象業務組件以前,請問本身:

* 存在代碼重複嗎?若是隻使用一次,或者只是某個特定用例,可能嵌入組件中更好。

* 若是它只是幾行代碼,分隔它反而須要更多的代碼,那是否能夠直接嵌入組件中?

* 性能會收到影響嗎?更改state/props會致使從新渲染,當發生這種狀況時,你須要的是 只是從新去渲染通過diff以後獲得的相關元素節點。在較大的、關聯很緊密的組件中,你可能會發現狀態更改會致使在不須要它的許多地方從新呈現,這時應用的性能就可能會開始受到影響。

* 你是否有一個明確的理由?分離代碼我想要實現什麼?更鬆散的耦合、能夠被複用等,若是回答不了這個問題,那最好先不要從組件中抽離。

* 這些好處是否超過了成本?分離代碼須要花費必定的時間和精力,咱們要在業務中去衡量,有所取捨。
複製代碼

封裝

良好的組件封裝應該隱藏內部細節和實現意義,並經過props來控制行爲和輸出。

減小訪問全局變量:由於它們打破了封裝,創造了不可預測的行爲,而且使測試變得困難。能夠將全局變量做爲組件的props,而不是直接引用。

組合

具備多個功能的組件,應該轉換爲多個小組件。
單一責任原則描述瞭如何將需求拆分爲組件,封裝描述瞭如何組織這些組件,組合描述瞭如何將整個系統粘合在一塊兒。
複製代碼

純組件和非純組件

非純組件有顯示的反作用,咱們要儘可能隔離非純代碼。

將全局變量做爲props傳遞給組件,而非將其注入到組件的做用域中。

將網絡請求和組件渲染分離,只將數據傳遞給組件,保證組件職責的單一性,也能將非純代碼從組件中隔離。
複製代碼

可測試

測試不只僅是自動檢測錯誤,更是檢測組件的邏輯。

若是一個組件測試不易於測試,很大多是你的組件設計存在問題。
複製代碼

富有意義

開發人員大部分時間都在閱讀和理解代碼,而不是實際編寫代碼。
有意義的函數、變量命名,可讓代碼具備良好的可讀性。
複製代碼

組件設計的最佳實踐

組件的UML類圖

前端組件的架構實際上是一個是樹狀圖,當咱們設計一個組件時,推薦用UML類圖的形式,將組件結構、數據流動狀態、處理函數明確標註。先構思組件細節,再寫代碼,能夠避免代碼的屢次反覆。

  • 用UML類圖的形式,將網頁中的組件樹結構畫出來
  • 每一個類圖中,標明須要的state、props、methods,

咱們想要實現一個Table組件,Table組件包含行數(RowCount)、header、body。

Table的數據源data、行數RowCount,都來自props;Table內部的排序函數,須要的狀態sortPerperty、ascending來自state;

Table的操做包括onRowClick,來自props;排序setSortProperty,來自state;

UML類圖以下所示,能夠直觀的瞭解組件的UI層結構,數據流動和處理函數,寫代碼時不再怕會重構了^_^

輔助代碼分離

爲了讓下一個接手的同事更好的理解代碼,咱們有時會在覈心代碼中,添加必要的註釋,讓代碼更清晰。

let params = {
    pageNum: pageNum,
    pageSize: pageSize,
    status: 4, // 參照:ROBOT_STATUS, 0-新導入,1-審覈中,2-審覈經過,3-審覈未經過,4-上架,5-下架
    title: search,
    queryType: 0 // 0--只查詢列表, 1--查詢申請狀態
};
複製代碼

代碼如上,閱讀代碼時,你能快速瞭解各個字段的含義,但你會額外分心去看註釋,思路被打倒,致使中斷了整個函數的邏輯分析。

所以,假數據、非技術說明文檔、配置代碼,建議放在代碼外,而不要放在覈心代碼中,會影響用戶體驗。

扁平化的state和props

給組件傳遞props時,建議用更扁平化的props,而不要用嵌套的對象或數組。

<DetailModal {...modalData} visible={showModal} tagType={ROBOT_TYPE} sceneList={sceneList} handleCloseModal={() => this.handleCloseModal()} />
複製代碼
  • 如上面的代碼所示,傳遞的數據結構是modalData,但用戶不清楚modalData中包含哪些屬性,且還可能存在多餘的屬性,開發中應儘可能避免傳遞給組件不須要的屬性。
  • react中,若是要修改對象、數組,必須建立一個副本;可能會由於淺複製而形成頁面的從新渲染。

參考文章連接:

1.前端組件設計原則

2.可靠React組件設計的7個準則

3.通用前端組件的開發

4.從新設計React組件庫

相關文章
相關標籤/搜索