分頁是一個很簡單,通用的功能。做爲一個有經驗的前端開發人員,有義務把代碼中相似這樣公共的基礎性的東西抽象出來,一來是改善代碼的總體質量,更重要的是爲了未來作相似的功能或者相似的項目,能減小沒必要要的重複工做量。在實際項目中,尤爲是網站類型的項目中,分頁部分的設計老是個性化比較強,基本上都不會長的同樣,因此可能以前抽象出來的東西,若是寫的不夠靈活的話,對這些個性化強的項目來講,可能直接應用的時候也得作些調整才行。本文嘗試提供一個儘可能知足這兩方面要求的分頁組件。css
先介紹下寫這個東西的背景:一直以來,我都想寫一個相對比較靈活簡單的列表組件,去年寫過一個版本,後來改了幾回,如今已經用到好幾個項目裏面去了,重構起來也有很多的工做量,由於應用到的頁面已經把比較多了,因此就沒有輕易地去作這個事情。最近的工做,涉及到一個相對簡單的列表頁面,而後給的時間也比較多,因而我準備趁這個時候把我一直想寫的列表組件給寫出來。現有的那個列表管理組件,沒有作好職責分離,列表的管理跟分頁的管理是揉在一塊兒的,代碼也比較亂,因此此次我打算先從分頁組件下手。由於分頁組件與列表之間並無太多耦合的邏輯,因此當把它們分離出來的時候,代碼會更加清晰,獨立,未來要維護也方便些。前端雖然作不到像後臺那樣,考慮那麼多的設計模式,但要是能把代碼寫的更讓人容易理解的話,對團隊對公司來講,真的是一件很好的事情。html
雖然網上有很多的分頁插件,可是都不值得去用,一來是有輕微的學習成本,二來是不符合本身的封裝的思想,看着彆扭;並且像這樣簡單的封裝,最適合本身動手去寫,增強面向對象編程的鍛鍊了。前端
下面就開始這個分頁組件的內容。git
先來講下個人基本想法。分頁這個部分,從內容上可能包括有:上一頁,首頁,下一頁,尾頁以及具體頁;頁碼輸入跳轉;分頁大小;記錄總數;記錄範圍等;從結構上,必須知道分頁大小,當前頁的索引以及記錄總數才能構造全部的內容;從操做上:改變分頁大小,或者是點擊上一頁,首頁,下一頁,尾頁以及具體頁,或者是直接輸入頁碼,都會引起外部分頁內容的拉取。對外部來講,無論分頁部分作什麼操做,只要在這些操做以後,通知外部去拉取便可,分頁只須要提供一個簡單的api給外部,告訴它們當前的分頁大小和頁碼;可是在外部拉取到新的內容以後,還得作一件事情,就是要根據最新的記錄總數,去更新分頁部分的UI,前面說分頁的內容須要記錄總數,頁碼和分頁大小才能構造出來。因爲頁碼跟分頁大小都屬於分頁內部管理的,因此外部更新分頁UI的時候,只須要告訴分頁最新的記錄數就夠了。以上就是分頁組件跟外部功能互相交互時候的惟一場景,基於這些,就能夠把因此把分頁相關的東西都封裝起來,給外部提供幾個簡單的api來實現它們之間的調用關係,最終完成咱們須要的分離的目的。github
方便你們看到這個東西的實際用法跟效果,我模擬真實的場景,寫了一個簡單的demo,一塊兒來看看。算法
demo效果:chrome
demo地址:編程
http://liuyunzhuge.github.io/blog/form/dist/html/pageView.html設計模式
pageView相關css:api
https://github.com/liuyunzhuge/blog/blob/master/form/src/css/page_view.css
pageView.js源碼:
https://github.com/liuyunzhuge/blog/blob/master/form/src/js/mod/pageView.js
demo相關的源碼:
https://github.com/liuyunzhuge/blog/blob/master/form/src/js/app/pageView.js
源碼部分也能夠直接打開demo地址,經過chrome的開發者工具來查看,由於這些代碼放在git上面,是跟我之前的代碼放在一塊的,怕不知道的人還覺得這麼個小東西,還須要寫那麼多個文件。再補充下其它方面的說明:這些代碼都是用seajs管理的,而後整個pageView.js是用我本身以前寫的一個用來作js的面向對象編程的模塊class.js方式開發的,可能沒了解過的人,不知道它是作什麼用,關於它的介紹都在下面這篇文章裏面:
直接看我在demo裏,完成那個頁面的核心代碼:
從上面的代碼也能看出,這個分頁組件最核心的api也就是下面幾個:
getParams():它是一個實例方法,返回組件的分頁大小跟頁碼,至於這兩個值的參數名字,能夠經過option來修改;
refresh(total):這也是一個實例方法,外部傳給它一個最新的記錄總數,分頁組件再根據它從新渲染UI;
onChange:這是一個函數類型的回調,它能夠做爲一個option在建立實例的時候傳入,全部分頁操做都會反饋到這個回調裏面來,一般只要把外部拉取內容的動做綁定它上面便可。
考慮到要控制分頁的重複操做問題,還另外加了一個disable和enable的api,這個比較好理解了。當分頁被disable的時候,會顯示禁用的樣式,cursor爲not-allowed,全部分頁操做都將無效。
實際上,我在最近的一個項目中, 寫相似demo的一個簡單分頁功能,就是這麼寫的,業務代碼不多,邏輯也比較清晰,雖說列表管理,好比渲染那一塊,還能夠再封裝一下,可是在這種簡單場景中,再也不去封裝,也是能夠的,由於它自己已經很簡單,再想辦法封裝,也增長不了多少的靈活性。
如今還有很多的公司在採用沒有封裝的代碼,分頁的功能在不一樣的頁面都寫一遍,每一個位置都定義五六個function,好比goFirst goPrev goLast goNext goPage這種,從結果上來講沒啥很差,就是在作重複的事情的時候效率不高,很差維護。要是都能封裝起來,相信能給團隊帶來很多好處。
先從option提及,如下是pageView.js定義的全部option及默認值:
var DEFAULTS = { defaultIndex: 1,//默認頁 defaultSize: 10,//默認分頁大小 pageIndexName: 'page',//分頁參數名稱 pageSizeName: 'page_size',//分頁大小參數名稱 onChange: $.noop,//分頁改變或分頁大小改變時的回調 onInit: $.noop,//初始化完畢的回調 allowActiveClick: true,//控制當前頁是否容許重複點擊刷新 middlePageItems: 4,//中間連續部分顯示的分頁項 frontPageItems: 3,//分頁起始部分最多顯示3個分頁項,不然就會出現省略分頁項 backPageItems: 2,//分頁結束部分最多顯示2個分頁項,不然就會出現省略分頁項 ellipseText: '...',//中間省略部分的文本 prevText: '上一頁', nextText: '下一頁', prevDisplay: true,//是否顯示上一頁按鈕 nextDisplay: true,//是否顯示下一頁按鈕 firstText: '首頁', lastText: '尾頁', firstDisplay: false,//是否顯示首頁按鈕 lastDisplay: false,//是否顯示尾頁按鈕 };
我把其中須要再詳細解釋下的說明清楚。
1)pageIndexName和pageSizeName
這兩個用來定義分頁參數的名字,還記得那個getParams方法嗎,它是這樣的:
getParams返回一個對象,這個對象包含兩個鍵值對,鍵分別用pageIndexName和pageSizeName這兩個option,值就用分頁內部管理的分頁大小和頁碼。當外部調用這個方法時,就能直接把它的返回值做爲查詢參數傳遞到後臺接口了。
2) middlePageItems,frontPageItems,endPageItems
也許看了demo裏面的分頁部分的效果就能明白它們三個的做用:
middlePageItems表明中間連續部分的分頁項的數量;
frontPageItems表明起始部分連續的分頁項的數量;
endPageItems表明結尾部分連續的分頁項的數量。
這三個option之因此要定義,是由當前這個分頁組件要作的效果,以及它使用的分頁算法決定的。
而後pageView定義的實例方法就不過多說明了,由於都比較簡單,並且最核心的都已經在demo裏面體現出來,感興趣的話,照着用便可。若是對代碼感興趣,碰到有疑問的,歡迎私信一塊兒交流。
最後說下分頁算法。影響分頁組件能不能通用的另一個要素就是分頁算法。有的可能不須要分頁算法,直接從1到總頁數顯示出來就完了,可是這樣確定有問題,尤爲當總頁數不少的時候;有的分頁跟我這個就不太同樣,它可能只顯示當前頁在內的連續一部分頁碼,而後當切換不一樣的頁的時候,這個連續部分也不相同;我這裏用的是較爲常見的一個算法,首尾以及中間都有連續部分。詳細的渲染邏輯都在render方法裏面,可是最核心的東西其實仍是getInterval這個函數:
function getInterval(data, opts) { var ne_half = Math.ceil(opts.middlePageItems / 2); var np = data.pages; var upper_limit = np - opts.middlePageItems; var start = data.pageIndex > ne_half ? Math.max(Math.min(data.pageIndex - ne_half, upper_limit), 0) : 0; var end = data.pageIndex > ne_half ? Math.min(data.pageIndex + ne_half, np) : Math.min(opts.middlePageItems, np); return [start, end]; }
它的做用在於返回中間連續部分的起止索引。根據這個起止索引渲染中間部分的頁碼,而後把start和frontPagetItems比較,渲染起始部分的頁碼;把end與與backPageItems比較,繪製結尾部分的頁碼。
以上就算是這個分頁組件的所有核心內容了。可是最終來看,它仍是有些問題的。一開始我就說過,這種東西要是能作到足夠靈活,可以知足不一樣項目裏面相同功能的話,這樣就纔算強大。基於這點來看目前的pageView,它的問題在於:
1)固化了分頁算法,要是換一個項目,產品不想搞這個分紅首尾中間連續三部分的效果,那麼就必須改動源碼才能適應需求了。要解決這個問題,能夠考慮把pageView再抽象出一個父類,不一樣的子類去覆蓋render方法,也就是在項目中提供多個pageView的實現,要用哪一個,根據需求來定。
2)沒有包括分頁大小,頁碼跳轉,記錄總數和記錄範圍這些內容,有可能其餘項目須要這些東西,做爲分頁的輔助功能。要解決這個問題,能夠考慮在當前的版本上擴充,補充事件的監聽,添加一些合適的option,控制這些內容是否顯示便可。
我之因此沒有去解決上面的這些問題,有兩個緣由:
1)就目前的全部場景來講,沒有碰到要額外內容的場景,如分頁大小等,因此先不處理,避免增長這個組件的複雜性;
2)對於分頁算法,我認爲在產品設計中沒有必要作出太多的不一樣的設計出來,因此固化一種沒有問題。由於無論從哪一方面,爲不一樣的頁面提供不一樣的分頁算法都是一件很划不來的事情,若是當產品出現這種問題的時候,我會盡力去把他說服。
固然,每一個人想法不一樣,堅持本身的思想最好。
最後但願本文多少對你們有點用處,謝謝閱讀:)