列表元件抽象(1):概述

發表於2016-09-19

這次要介紹的是列表元件。為了寫它,我花了有將近2周的晚上,才一點一點的把它寫到現在這個程度。到目前為止,一共寫了有17個檔案,雖然沒有覆蓋到一些更復雜的場景,但是把我當時計劃寫這個元件的基本目的已經完成了。先給大家看看我最後寫出來的檔案情況:

459873-20160917211848430-1897641763

也許有人會好奇,一個列表的功能怎麼會寫出這麼多東西出來?關於這個問題的答案,我稍後再來總結,先讓我描述下我寫這些東西之前產生的想法。

1. 背景介紹

我是去年5月份在上家公司開始做的前端開發,在那個公司,我花了2周的時間,盡力寫了一套js的元件,用於我們當時的一個管理系統的開發。大家都知道,在管理系統裡面,最常見的兩種頁面型別無非就是列表頁面和編輯頁面。拿列表頁面來說,它裡面用到的列表元件通常基於table進行開發的,當時考慮過jquery.datable這樣的外掛,後來放棄了。原因是:jquery.datatable有很多多餘的東西,我不需要;它的框架在使用時候的思路,跟我以前使用的其它公司開發平臺裡面列表元件完全不同,我覺得我以前使用的元件思想更清晰簡單。所以我就憑自己當時的能力開發出了一個能滿足以下一些功能的table元件:

1. 分頁;2. 列寬拖拽;3. 序號列自動生成;4. 單選和多選;5. 列表頭固定,以便在列表區域滾動的時候,列表頭不會受滾動的影響;6. 樹形列表;7. 列表各種事件等。

後來我不單是做管理系統了,還做了更面向普通用於的網站類應用和移動端應用。這個時候又根據產品的需求開發出了三個另外的列表元件:第一個是帶分頁工具欄的列表元件,這個元件相當於就是前面那個table元件的縮小版,只不過html結構上不再使用table,而且也不需要拖拽,樹形控制那樣的功能了;第二個在第一個的基礎上去掉了分頁工具欄,改成了利用window的滾動事件進行滾動翻頁,當然為了留一個後路,這個元件提供了載入更多這樣的手工進行下一頁的按鈕;第三個使用於移動端,由於移動端scroll事件必須得等到螢幕的滾動操作完全停止下來以後才會觸發,所以不能直接利用window的滾動事件,而改用了iscroll外掛來模擬滾動,同時利用它提供的scroll事件來實現更加快速響應的滾動翻頁功能。

其實在我後來寫第二個元件的時候,我當時就在考慮:不管是後臺的列表也好,還是前臺的列表也好,除了表現出來的樣子不一樣,它們在傳送請求的方式,解析響應的方式,以及列表渲染的方式應該都是差不多的,而且在後臺的列表中,類似分頁排序這樣的功能,在前臺的列表裡面也有較為常見的使用場景,既然如此,這些相似的東西,如果能抽象到一個父類裡面,然後由前後臺的列表元件去實現各自獨有的部分,這樣的程式碼的結構會不會更加簡練一點。因為當時後臺的那個列表元件,我已經寫了1000多行了,自己有時候想改個東西,都要滾來滾去地閱讀以前寫的程式碼,十分不便。

再拿後面的三個元件來說,滾動分頁與不滾動分頁元件的區別,僅僅在於分頁的控制不同而已,如果把分頁這一個部分完全丟出去,那麼剩下的部分,就不需要在兩個元件裡面都去實現了,只用在一個類裡面實現一次。iscroll與window的scroll,也僅僅只是滾動事件的釋出者不同而已。如果我提供兩個不同的滾動翻頁的元件,一個使用window scroll實現,一個使用iscroll實現,然後由列表元件去決定要使用哪個分頁元件,那麼程式碼會更加簡潔。

假如按以上的思路實現,我之前寫的那些程式碼,可以得到以下這些方面的改進:

1. 通過繼承解決掉程式碼重複問題,而且增強了程式碼的可維護性;

2. 通過職責分離,比如把分頁功能,排序功能從元件中分離出來,然後元件內部採用注入的方式進行使用,這樣一個列表想要更換分頁、排序等擴充套件功能就會變得很容易。

所以這也就是我寫前面截圖那些檔案的由來。至於為啥會有這麼多個,這都是由於抽象出了一些公共的基類以及把分頁,排序等功能從列表元件內分離出來之後的結果,而且這些檔案實際上包含了前面提到4種列表的所有功能,所以總的檔案數就比較大了。在實際使用中,要使用某一個型別的列表的時候,基本上只要使用simpleListView, tableView , scrollListView , iscrollListView即可。另外拆分成這麼多檔案之後,每個檔案就變得很小了,基本上都是200行左右,沒有一個超過400行的,更容易閱讀理解。

由於這一次的內容比較多,這篇文章無法詳細的介紹所有內容,只能先把檔案之間的結構以及應用場景介紹一下,後面幾篇文章會針對一些我覺得有必要進行解釋說明的內容進行補充。歡迎感興趣的朋友繼續關注後面的文章,我會在近期陸續總結出來,好在最近的工作應該不會像之前那麼忙。原始碼已經上傳到github,可通過下面幾個地址來獲取元件原始碼以及demo的原始碼:

元件原始碼:

https://github.com/liuyunzhuge/blog/tree/master/form/src/js/mod/listView

demo原始碼:

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_1.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_2.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_3.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_4.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/tableView.js

接下來介紹下各個檔案的具體作用。

2. 檔案結構

1. base/listViewBase

它是所有列表元件的基類,基本上所有公共的事情,都是在它裡面處理的,比如排序元件初始化,分頁元件初始化,模板引擎管理元件初始化,以及請求傳送和請求解析等。每一個具體的列表管理元件都需要繼承它。在這個類裡面使用了模板方法的設計模式,以及在請求資料的前後,新增了多個事件回撥,目的是為了子類能夠進行靈活的個性化擴充套件。

2. base/pageViewBase以及simplePageView

pageViewBase是所有分頁元件的基類。分頁元件不管內部如何實現,其實對外需要提供的介面或者事件都是相同的。對於列表元件來說,只需要知道分頁元件什麼時候分頁引數發生改變了而已,然後在改變的時候,帶上最新的分頁引數重新請求資料。這個類從簡單封裝分頁功能pageView.js這篇文章中提到的pageView.js裡面抽象出來。那篇文章介紹的pageView.js的功能,現在只需要定義一個繼承pageViewBase的類,然後把pageViewBase未實現的那些具體邏輯新增進去即可,也就是現在的更加簡潔的simplePageView。

3. base/sortViewBase和sortFields

sortViewBase是所有排序元件的基類。排序元件的實現思路,其實跟分頁元件的思路很像,對於列表元件來說,只需要知道排序元件什麼時候排序狀態發生了變化即可。就像很多的table外掛一樣,一般列表都是單擊某列的標題即可使得列表的資料按該列進行不同方式的排序,排序元件負責實現的就是這些排序方式切換的操作邏輯。由於排序元件,最終要給列表元件提供排序引數,所以又把排序引數的管理分離出來,寫成了sortFields類。這樣做的目的主要是為了簡化sortViewBase的實現。在程式設計中,把資料的邏輯與UI的邏輯分開,往往是寫出更好程式碼的關鍵。

4. base/tplBase和mustacheTpl

base/tplBase是模板引擎管理元件的基類。模板引擎管理元件用於渲染列表的資料。它宣告瞭兩個介面,compile和render,用來進行模板的編譯和模板的渲染。之所以要這麼寫,而是因為假如有人想採用這一套程式碼的話,也許我這裡面用到的模板引擎並不是他們團隊人員所熟練使用的,所以為了能夠更加靈活的適用其它模板引擎,我就把模板引擎的使用給抽象了。同時由於我在程式碼中都是使用mustache作為模板引擎,所以我也提供了繼承tplBase的mustache版本的實現mustacheTpl。

5. simpleListView 和simpleSortView

看名字也能知道它們的作用了。simpleListView 繼承自listViewBase,simpleSortView繼承自sortViewBase。simpleListView 實現最簡單的分頁列表管理元件。simpleSortView給這個列表管理元件提供排序管理的功能。

6. scrollListView和scrollPageView

scrollListView繼承自listViewBase,scrollPageView繼承自pageViewBase。scrollListView實現相對於瀏覽器視窗或者某一個DOM元素進行滾動翻頁的列表元件,這個列表元件與simpleListView的區別不僅僅是翻頁的不同,同時在資料的渲染和UI互動上也有區別,畢竟翻到下一頁的時候,需要一些更加友好的載入提示,以及不能刪除之前已載入的資料內容。scrollPageView用來實現載入更多的翻頁功能。

7. iscrollListView和iscrollPageView

跟前面不一樣的是iscrollListView繼承自scrollListView,畢竟它跟scrollListView是有很多共性的,不過它比scrollListView多了一個iscroll例項的管理功能,因為它要用iscroll外掛。iscrollPageView提供給iscrollListView針對iscroll外掛的載入更多的翻頁功能。

8. tableView

它繼承自scrollListView。用來實現基於table的列表元件。支援單多選,支援獲取選中行的資料,支援按索引獲取行的資料,支援表頭固定等。它是所有列表元件中相對複雜的一個。但實際上只是功能多,邏輯並不複雜。

9. tableDrag和tableOrder

我把它們作為外掛提供給tableView,分別為它提供列寬拖動以及序號列生成的功能。這麼做的目的也是考慮到儘可能地簡化tableView的功能。畢竟列寬拖拽這些功能都屬於可選性質的。假如後面我想給tableView增加一個樹形列表的管理功能,那麼只要按照這兩個外掛的實現方式再寫一個類似tableTree的外掛即可。

10. tableDefault

這個純粹是為了簡化例項化tableView時,提供一些預設的配置,比如外掛的配置等等。

以上就是這些檔案的基本作用。下面會結合demo來演示四個列表元件的實際使用方式。

3. simpleListView

demo地址:http://liuyunzhuge.github.io/blog/form/dist/html/listView_1.html

demo檔案:https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_1.js

實際效果:

459873-20160917211850617-470710185

頂部列印了列表元件在請求資料時傳遞給後臺的引數。排序元件由於涉及到可能有多個排序欄位,所以用陣列字串進行傳遞,各個欄位在陣列中的順序,代表它們在資料庫中進行排序時的先後關係。

相關使用程式碼如下:

4. scrollListView

demo地址:

http://liuyunzhuge.github.io/blog/form/dist/html/listView_2.html

http://liuyunzhuge.github.io/blog/form/dist/html/listView_3.html#

demo檔案:

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_2.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_3.js

這個之所以有兩個demo,那是因為第一個demo是相對於window進行滾動翻頁的,第二個demo是相對於某個DOM元素進行滾動翻頁的。

實際效果這裡就不展開了。畢竟這個demo要滑鼠滾動操作才能看到更真實的效果。可通過前面的連結進行檢視。

實際使用的程式碼也不展示了,因為跟前面的simpleListView差不多。

5. iscrollListView

demo地址:

http://liuyunzhuge.github.io/blog/form/dist/html/listView_4.html

demo檔案:

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/listView_4.js

預覽的話可以按F12切換的手機模擬器進行demo預覽,iscroll畢竟是移動端用的東西,pc端也能遇到,要用滑鼠拖動才能進行滾動,我沒有啟用滾輪操作。其它的跟前面差不多。

6. tableView

demo地址:

http://liuyunzhuge.github.io/blog/form/dist/html/tableView.html

demo檔案:

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/tableView.js

實際效果:

459873-20160917211852195-1343651972

tableView元件跟其它列表元件很大不同的一點是,它對html結構要求更加嚴格:

459873-20160917211853820-1876564917

好在這個結構其實是非常清晰的。之所要弄成這個,跟表頭固定的需求有關係,然後表頭滾定又會帶來其它相關的問題,這些我會在後面的文章再細說。

使用的方式是:

在demo程式碼中還可以看到,tableView元件使用colgroup和tableHd兩個option來定義列寬描述以及表頭內容的html模板,之所以不直接寫在html裡面,也是為了html的簡潔考慮。如果放在js裡面的話,反而更統一一些,畢竟表體部分的html模板就是js中進行定義的。

通過以上這些demo可以看到,雖然前面定義了很多檔案,但是在使用的時候,這些元件用起來並不複雜,要是結合我之前寫的那些跟form相關的元件,那麼去做一些帶查詢列表的列表管理功能也不是很難,基本只需要list.query(appForm.getData())即可。歡迎感興趣的朋友前去使用,這些都是我已經在實際專案中使用了以後感覺比較省時省力的方法,不比一些有名的jquery外掛差。

7. 本文小結

總之,我是想到所有的列表的功能都具備相似性,不想在一個專案中,元件層面存在重複的程式碼,所以就寫了這些東西,力求今後我在新專案中,能夠將程式碼的重複度降到最低,方便維護跟擴充套件。本文也只是把我這段時間的成果起了個頭,大體上介紹了下我寫的這些列表元件的作用以及用法,但是裡面還有很多的細節還沒有完全說明,為了讓人看得更加明白,我這幾天會加速把相關內容總結出來。最後,希望這些東西不管是學習還是工作方面,都能給一些朋友帶來實際的幫助。如果覺得裡面有什麼不好的、不對的,歡迎幫我指出。

相關文章