前段時間寫了一個列表框架公共組件(雖然是項目內的公共組件,但很多原則和真正的公共組件一樣),可以快速靈活搭建各種列表頁面,且具有高擴展性,已經完美經歷了多個需求迭代。
這也算是我花了很多心思的得意之作了[憨笑],很多設計思路一直想分享出來,總結了一下,抽取一個公共組件,至少應當仔細考慮一下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 也存在這樣的錯誤,后面使用時發現后才改正的,這里就不說了,因為上面舉的例子已經能很好的說明這個注意點了。
總之設置配置項時,保持高內聚,低耦合的原則是不變的!