本文是我學習 ElementUI 源碼的第六篇文章,上一篇文章學習了 ElementUI 中 InfinityScroll 元件的實作,這篇文章來學習一下 ElementUI 是如何實作 Pagination(分頁)元件的。
分頁在網頁中是非常常用的功能,如何制作分頁元件也是前端工程師需要掌握的一個技巧。
元件效果
ElementUI 的分頁元件提供的功能比較豐富,除了正常的翻頁功能外,還提供了跳頁、設定每頁數量、增加自定義元件等功能,效果如下:
pagination
元件實作
首先來看一下分頁元件的項目結構:
struct
其中的
pagination.js
是真正對外提供的分頁元件,它聚合了包括上下頁按鈕、跳頁、總量、每頁數量、頁碼等元件,
pagination.js
是使用
JSX
文法編寫的,好處在于可以少定義很多的判斷變量同時又沒有純
render
函數那麼複雜;而
pager
就是頁碼元件。
pagination 元件
打開
pagination.js
,先看看引入的内容:
Pager
是頁碼元件;
ElSelect
和
ElOption
用于設定每頁數量;
ElInput
用于實作跳頁;
Locale
是用于實作國際化的,本文不會講解;而
valueEquals
是用于判斷兩個變量是否相等的(針對基本類型和數組),其内容如下:
這段代碼源于 StackOverflow 上的一個回答,ElementUI 對其做了一些優化,感興趣的朋友可以檢視原回答。需要注意的是,這段代碼判斷的數組隻是簡單的一維數組,對于對象數組和多元數組是無效的。
接下來我們開始閱讀
pagination.js
的元件代碼,
props
屬性不需要多講,直接跳到
data
部分,其内容如下:
可以看到,其中定義了四個屬性,
internalCurrentPage
和
internalPageSize
是元件内部真正使用的目前頁和每頁數量。
在使用分頁元件時,我們會為目前頁(或每頁數量)綁定屬性,而這些屬性值在傳入分頁元件後,是不能直接在分頁元件内容進行修改的,是以分頁元件内部需要維護一份可變屬性。
是以,在
watch
中,ElementUI 定義了更新這兩個屬性的監聽事件:
對
internalCurrentPage
的監聽是為了實作
.sync
修飾符的同步更新功能。
可以看到
watch
中還有一個對
internalPageCount
的監聽,這個屬性是定義在
computed
中的,它用于計算實際的頁碼總數:
在使用分頁元件時,我們既可以傳入
total
,也可以傳入
pageCount
,甚至同時傳入兩者,為了得到恰當的頁碼總數,ElementUI 定義了
internalPageCount
屬性。
從代碼中我們可以看出,
total
屬性的優先級比
pageCount
高。
lastEmittedPage
和
userChangePageSize
稍後再講。
接下來我們進入
pagination.js
的
render
函數,其内容如下:
template
是最終用于渲染的模版,在定義時是最頂級元素,其内容會根據使用者傳入的
layout
屬性從
TEMPLATE_MAP
對象中選出需要使用的子元件,放到
template
的
children
中,而子元件也是采用
JSX
文法直接寫在
components
中的,後文将重點講解一下
pager
元件,其餘的就不展開了,沒有非常特别的内容(稍微提一下,子元件中大多使用
this.$parent
直接調用的父元件的屬性和方法),感興趣的朋友可以自行檢視。
再往下,我們将目光集中到
rightWrapper
上。
不知道朋友們有沒有發現,
layout
中的書寫順序是會影響分頁元件最終的呈現效果的,其中影響最大的就是
->
,當
layout
中出現此字元串時,處于其後的布局元件将被放入
rightWrapper
中,而它的布局方式是
float:right
。
rightWrapper
舉個例子:
效果分别是:
layout
接下來我們看看
pagination.js
中的
methods
,其内容如下:
這些方法并沒有太多特别的地方,這裡重點說一下
handleCurrentChange
和
emitChange
方法,前文遺留的
lastEmittedPage
和
userChangePageSize
屬性的作用在這裡展現出來了。
handleCurrentChange
方法是傳入到
pager
元件的,用于處理點選頁碼事件,它擷取頁碼的方法是通過
Number(event.target.textContent)
從元素的文本中提取的,而
pager
元件中的點選事件是通過事件委托交給
ul
元素處理的(後文會講)。當點選目前頁碼時,事件也會觸發,是以用
lastEmittedPage
來辨別是否是點選的目前頁碼,以防止觸發
current-change
事件。
userChangePageSize
的作用也是一樣,當使用者修改每頁數量時會造成
pager
元件重新渲染,頁碼更新,是以需要觸發
current-change
事件。
pager 元件
之是以要講一下
pager
元件,主要是因為它對于頁碼的處理過程值得學習一下。
首先看一下元件結構:
關注兩點,第一,頁碼的點選事件都通過事件委托交給了
ul
元素處理,也就是
onPagerClick
,性能更佳;第二,第一頁和最後一頁以及快速跳頁按鈕是直接定義好的了,重點是頁碼如何生成。
我們來看一下
pagers
的代碼:
可以看到
pagers
是一個計算屬性,它會根據
currentPage
和
pageCount
的值而變化。
不知道有沒有朋友細心觀察過,當分頁元件同時出現前後快速跳頁按鈕時,目前頁碼永遠處于最中間,而且通過
pagers
生成的頁碼數量為
pager-count
設定的值減去 2,因為第一頁和最後一頁是直接定義好了的。
pager
根據 ElementUI 的約定,
pager-count
的取值範圍為 5 - 21 之間的奇數,這樣能讓分頁元件有統一的良好的視覺效果。
頁碼的生成邏輯就是上述代碼中的
if
語句,感興趣的朋友可以仔細研究一下 ElementUI 是如何進行判斷的,并不複雜。
處理頁碼點選事件的函數
onPagerClick
内容比較簡單,就不再展開了,感興趣的朋友可以自行查閱。
結語
通過學習分頁元件的源碼,我們可以發現一個長期以來開發者争論的問題,即模闆文法好還是
JSX
好,其實兩者各有優勢,有各自适用的場景。就如同
pagination.js
中選取子元件的邏輯部分,如果用模闆文法的話會在 HTML 中增加很多的
v-if
,閱讀起來很不友善。
首圖由InspiredImages在Pixabay上釋出