公共組件設計要點總結


前段時間寫了一個列表框架公共組件(雖然是項目內的公共組件,但很多原則和真正的公共組件一樣),可以快速靈活搭建各種列表頁面,且具有高擴展性,已經完美經歷了多個需求迭代。

開發背景:有一個內容管理系統(項目采用vue框架,UI框架使用view-design),內容種類不斷的在擴展,最開始的文章,后來的游戲,接下來的教程……

每一種內容都有:待處理列表,已提交列表,已發布列表,回收站列表,草稿箱。

每一種列表的特點都不太相同,卻基本是一個模式:

  1. 篩選排序條件各不相同,有的列表沒有篩選排序條件,改變條件立即同步刷新列表(使用時,可以靈活定制各自的篩選排序)
  2. 有些列表需要記住並回顯上次的選擇的條件,甚至記住並標記出上次查看的列表項
  3. 有的列表需要對列表項進行多選,在此基礎上進行批量操作(如批量刪除,批量上下架),或者批量上傳配音等,有的列表什么都不需要(可以擴展其他批量操作)
  4. 有的列表需要周期性的更新數據【點開看看,相信你會有所領悟】,有的不需要 由於數據的差異,已有列表不能復用。

於是便開發出一個列表框架 ListFrame: 使用它,只需要提供接口和列表item樣式,就可以快速定制各種列表;改變搜索及時刷新,分頁控制,多選控制,周期更新,條件記憶等邏輯,完全不用關心了,ListFrame 可靈活配置開啟。

 

這也算是我花了很多心思的得意之作了[憨笑],很多設計思路一直想分享出來,總結了一下,抽取一個公共組件,至少應當仔細考慮一下4點:

1、公共組件不應該集成定制性強的功能,可以集成通用或較固定的功能,但應默認關閉那些不常用的功能

只有極少數列表用到的功能就不要封裝了,封裝他們 增加了公共組件的代碼量,但又沒有很高的利用率。

幾乎所有的列表都可以用到,但不同種類的列表 表現不一樣(如內容類別搜索,文章類型的類別,游戲類型的類別 完全不同),就是定制性很強的功能了。
定制性很強的功能封裝起來需要包含各種情況,邏輯較復雜,另外也使列表的邏輯分散,不利於維護。試想一下,下次再擴展一個類型,豈不是還要修改這個ListFrame !
(開閉原則:公共組件不能保持穩定的,就是失敗的)

這有點小兒科了吧!
實際上識別出哪些功能是通用的(應該封裝的),哪些是常用的(應該默認開啟的),不能光靠拍腦袋,使用公共組件的過程,不斷修正總結 很重要。
維護公共組件,你一定要有一顆開放的心態。組件好不好,由使用的情況說了算,敢於承認失敗,才能不斷改進。

2、參數的配置要簡單且靈活

如果你想讓你的組件功能強大,通過配置就能快速擴展,那么一定要避免將配置設計的過於復雜。
配置復雜,無異於將工作量轉移到了寫配置上,沒有任何好處,反而讓代碼更難理解(額外的 要先理解你的公共組件)。

將配置設計的簡單關鍵就是:用好默認值 類型重載,且采用大家一看就明白的名稱。

構建列表框架,你必須要考慮分頁:
有些列表(如展示全部內容的列表)是沒有分頁
分頁有pageNo和pageSize屬性,且初始值是可以設定的,ajax請求時攜帶的參數名最好也是可以配置的
於是可能你的組件應該這樣使用:

  <list-frame :showPager="{
show:true, pageNo:{ name:'pageNo', initial:1 }, pageSize:{ name:'pageSize', initial:10 }}"
></list-frame>

后台在設計接口時,通常參數名就是 pageNo 和 pageSize;前端使用場景中基本上初始的pageNo是1,pageSize為10。采用默認值,所有的配置都可以省略:

<list-frame :showPager="{show:true}"></list-frame>
<!-- 沒有傳入的字段,就使用默認值,而非空(做好空值適配很重要) -->

為了更簡單,再加上類型重載。完整設計代碼如下:

class PagerConfig{
    show?:boolean,
    pageNo?:{
        name?:string,
        initial?:string
    }
    pageSize?:{
        name?:string,
        initial?:string
    }
}

// 類型重載
 showPager:Boolean | PagerConfig

// 默認值的定義與使用:
const defaultPager = {
  show: true,
  pageNo: {
    name: 'pageNo',
    initial: 1
  },
  pageSize: {
    name: 'pageSize',
    initial: 10
  }
};

this.pagerConfig = Object.assign(
  {},
  defaultPager,
  typeof this.showPager === 'boolean' ? { show: this.showPager } : this.showPager
);
// this.pagerConfig 就是最終的配置了!

那么使用時,就可以這樣寫了

<list-frame :showPager="true"></list-frame>
或:
<list-frame show-pager ></list-frame>

現在可以看到,一切變的非常簡單,同時又保持了足夠的靈活性!(靈活性是指,任然可以靈活的配置參數名稱 和 初始值)

補充於2020年9月8日15:38:40:

為什么要將pageNo等請求參數名稱設置為可配置的?

很多新手在開始寫公共組件時,喜歡將字段名設置為固定的:你要用我的組件,必須提供xxx這樣的對象!這樣設計也許能滿足大多數的典型場景,但一旦遇到和你的前置要求有沖突的場景(比如剛好pageNo字段有了別的含義——這很常見,比如在試卷相關的項目中“試卷編號”很可能被命名成pageNo,而分頁頁碼就只能用其他的了),公共組件就歇菜了。

讓一切可配置,去掉“要使用我的組件,你必須……”這樣的情況!

3、相關的配置要集中

 將相關的配置集中在一起,更利於用戶使用,可讀性更強。

ListFrame 封裝了 批量刪除 和 批量上下架 的功能。為什么要將這兩個功能封裝呢?因為:
1、有很多業務無關邏輯值得我們去封裝,如disable校驗,刪除和下架時要彈出輸入框,要求用戶輸入刪除原因或下架原因;
2、通用性很強,幾乎所有的列表都有刪除功能,所有已發布列表都有上下架功能;
3、不同列表除了對應的上下架接口,刪除接口不同外,好像沒有什么其他不同的。

看看下方代碼:

    <!-- 集中配置前: -->
    <list-frame
      :getPageFn="getPage"
      :itemsDelAble="true"
      :delFn="delFn"
      :shelfOperateAble="true"
      :offshelfFn="offshelfFn"
      :onshelfFn="onshelfFn"
    ></list-frame>

如果開啟批量刪除 除了設置 itemsDelAble 為 true,還要提供 delFn 刪除方法;
如果開啟批量上下架 除了設置 shelfOperateAble 為 true ,還需要提供 offshelfFn 和 onshelfFn 兩個方法。

既然提開啟功能和供方法必須同時存在,為什么不把他們綁定在一起呢?我們將 itemsDelAble 和 shelfOperateAble 有bool類型變成了 object 類型,於是很容易實現了集中配置的目標。

對比一下將配置集中前后的代碼,是否后者更容易理解,更方便使用了呢?

    <!-- 集中配置后: -->
    <list-frame
      :getPageFn="getPage"
      :itemsDelAble="{delFn}"
      :shelfOperateAble="{offshelfFn,onshelfFn}"
    ></list-frame>

(相關配置集中起來,實際上是對你的組件參數,按提供的功能模塊進行划分!)

4、避免出現職能交叉的配置

我發現,這一點錯誤,即便是大型UI框架也會犯。我現在開發的項目所采用的UI框架,關於Button就存在這個問題。下面就用個api截圖來說明:(就不發 鏈接,也不指明是哪個框架了)

 

按鈕的 type 大部分值都是表示主題顏色的,如default,primary,info等,但 text(文本按鈕) 和 dashed(虛線框按鈕)除外,我覺得這兩個應該是屬於shape(形狀)的值。
這樣的 type 和 shape 職能交叉了。現在要實現一個文本按鈕,有不同顏色 —— 竟然做不到了!

我們在開發 ListFrame 也存在這樣的錯誤,后面使用時發現后才改正的,這里就不說了,因為上面舉的例子已經能很好的說明這個注意點了。
總之設置配置項時,保持高內聚,低耦合的原則是不變的!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM