分頁是WEB開發(fā)中很常用的功能,尤其是在各種前后端分離的今天粹懒,后端API返回?cái)?shù)據(jù)私恬,前端根據(jù)數(shù)據(jù)的count以及當(dāng)前頁碼pageIndex來計(jì)算分頁頁碼并渲染到頁面上已經(jīng)是一個很普通很常見的功能了堪澎。博主之前的公司分頁是用的一個jquery插件,使用起來真的是諸多不便辞槐。今天我們自己手寫一個vue2的分頁組件掷漱,供以后開發(fā)使用。
- 請求API榄檬,返回第一屏數(shù)據(jù)(pageSize內(nèi))以及所有相關(guān)條件的數(shù)據(jù)總量count卜范。
- 將數(shù)據(jù)總量傳遞給page組件,來計(jì)算頁碼并渲染到頁面上鹿榜。
- 點(diǎn)擊頁碼海雪,發(fā)送請求獲取該頁碼的數(shù)據(jù)锦爵,返回?cái)?shù)據(jù)總量count以及該頁碼下的數(shù)據(jù)條目。
簡單處理奥裸,樣式類似于bootstrap的分頁組件险掀,在第一頁時,禁用上一頁湾宙,以及首頁按鈕樟氢;在最后一頁時,禁用下一頁侠鳄,以及尾頁按鈕埠啃;超出范圍的頁碼以...來代替,效果圖如下:
好吧伟恶,下面曬出我的分頁組件模版:
<template>
<ul class="mo-paging">
<!-- prev -->
<li
:class="['paging-item', 'paging-item--prev', {'paging-item--disabled' : index === 1}]"
@click="prev">prev</li>
<!-- first -->
<li
:class="['paging-item', 'paging-item--first', {'paging-item--disabled' : index === 1}]"
@click="first">first</li>
<li
:class="['paging-item', 'paging-item--more']"
v-if="showPrevMore">...</li>
<li
:class="['paging-item', {'paging-item--current' : index === pager}]" //index是當(dāng)前頁碼
v-for="pager in pagers"
@click="go(pager)">{{ pager }}</li>
<li
:class="['paging-item', 'paging-item--more']"
v-if="showNextMore">...</li>
<!-- last -->
<li
:class="['paging-item', 'paging-item--last', {'paging-item--disabled' : index === pages}]"
@click="last">last</li>
<!-- next -->
<li
:class="['paging-item', 'paging-item--next', {'paging-item--disabled' : index === pages}]"
@click="next">next</li>
</ul>
</template>
模版寫粗來了碴开,是不是很一目了然的感覺,css就不在這里擺出來了博秫。
好了潦牛,接下來就是比較復(fù)雜的js代碼了,我們先思考一下挡育,這個組件肯定是作為子組件被引用到一個父組件里面巴碗。
export default {
name : 'MoPaging',
//通過props來接受從父組件傳遞過來的值
props : {
//頁面中的可見頁碼,其他的以...替代, 必須是奇數(shù)
perPages : {
type : Number,
default : 5
},
//當(dāng)前頁碼
pageIndex : {
type : Number,
default : 1
},
//每頁顯示條數(shù)
pageSize : {
type : Number,
default : 10
},
//總記錄數(shù)
total : {
type : Number,
default : 1
}
},
methods : {
prev(){
if (this.index > 1) {
this.go(this.index - 1)
}
},
next(){
if (this.index < this.pages) {
this.go(this.index + 1)
}
},
first(){
if (this.index !== 1) {
this.go(1)
}
},
last(){
if (this.index != this.pages) {
this.go(this.pages)
}
},
go (page) {
if (this.index !== page) { //點(diǎn)擊的頁碼不是當(dāng)前頁碼
this.index = page
//父組件通過change方法來接受當(dāng)前的頁碼
this.$emit('change', this.index)
}
}
}
其實(shí)這些method里面的prev即寒,next都還比較簡單良价,如果對父子組件通信不了解的同學(xué),文章結(jié)尾我會給一個例子蒿叠,關(guān)鍵字$emit。
data () {
return {
index : this.pageIndex, //當(dāng)前頁碼
limit : this.pageSize, //每頁顯示條數(shù)
size : this.total || 1, //總記錄數(shù)
showPrevMore : false, //前后有2個 ... 根據(jù)這里判斷是否顯示
showNextMore : false
}
},
computed : {
//計(jì)算總頁碼
pages(){
return Math.ceil(this.size / this.limit)
},
//計(jì)算頁碼蚣常,當(dāng)count等變化時自動計(jì)算
pagers () {
const array = []
const perPages = this.perPages
const pageCount = this.pages
let current = this.index
const _offset = (perPages - 1) / 2
const offset = {
start : current - _offset,
end : current + _offset
}
//-1, 3
if (offset.start < 1) {
offset.end = offset.end + (1 - offset.start)
offset.start = 1
}
if (offset.end > pageCount) {
offset.start = offset.start - (offset.end - pageCount)
offset.end = pageCount
}
if (offset.start < 1) offset.start = 1
this.showPrevMore = (offset.start > 1)
this.showNextMore = (offset.end < pageCount)
for (let i = offset.start; i <= offset.end; i++) {
array.push(i)
}
return array
}
watch : {
pageIndex(val) {
this.index = val || 1
},
pageSize(val) {
this.limit = val || 10
},
total(val) {
this.size = val || 1
}
}
}
大家可能看到pagers里面的代碼被嚇到了市咽,其實(shí)也沒什么,里面只是做了一些判斷抵蚊,提供左右2邊的... 是否現(xiàn)實(shí)的依據(jù)施绎。我們來分析一下:
- 中間顯示的條目是5,左邊的 ... 在當(dāng)前頁面大于3時顯示贞绳,這個很好判斷谷醉。
- 中間顯示的條目是5,右邊的 ... 在當(dāng)前頁面小于18時顯示冈闭,這個很好判斷俱尼。
- 當(dāng)前頁碼index在pagers數(shù)組的中間,這是一個奇數(shù)個的數(shù)組萎攒。
其實(shí)到了這里也差不多了遇八,我們來看一下父組件的寫法:
<template>
<div class="list">
<template v-if="count">
<ul>
<li v-for="item in items">...</li>
</ul>
<mo-paging
:page-index="currentPage"
:total="count"
:page-size="pageSize"
@change="pageChange">
</mo-paging>
</template>
</div>
</template>
這個父組件向下傳遞了一些數(shù)據(jù)給分頁組件矛绘,如total,page-index,page-size。
<script>
import MoPaging from './paging'
export default {
//顯示的聲明組件
components : {
MoPaging
},
data () {
return {
pageSize : 20 , //每頁顯示20條數(shù)據(jù)
currentPage : 1, //當(dāng)前頁碼
count : 0, //總記錄數(shù)
items : []
}
},
methods : {
//獲取數(shù)據(jù)
getList () {
//模擬
let url = `/api/list/?pageSize=${this.pageSize}¤tPage=${this.currentPage}`
this.$http.get(url)
.then(({body}) => {
//子組件監(jiān)聽到count變化會自動更新DOM
this.count = body.count
this.items = body.list
})
},
//從page組件傳遞過來的當(dāng)前page
pageChange (page) {
this.currentPage = page
this.getList()
}
},
mounted() {
//請求第一頁數(shù)據(jù)
this.getList()
}
}
</script>
mounted當(dāng)頁面掛載時刃永,就去獲取數(shù)據(jù)货矮。當(dāng)在分頁組件點(diǎn)擊了某個頁數(shù)后,會觸發(fā)父組件的pageChange事件斯够,這個函數(shù)里面的page是通過emit傳遞過來的囚玫。
好了,最后講一下父子組件通信的問題吧读规。抓督。。
父組件:
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter :increment="incrementTotal"></button-counter>
<button-counter :increment="incrementTotal"></button-counter>
</div>
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal() {
this.total += 1
}
}
})
子組件:
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment() {
this.counter += 1
this.$emit('increment')
}
},
})
上面講的兩種方法都父子組件之間的通信掖桦,有時候非父子關(guān)系的組件也需要通信本昏。在 Vue1.0 時代,可以通過 $dispatch 和 $broadcast 來解決枪汪,首先 dispatch 到根組件涌穆,然后再 broadcast 到子組件。Vue2.0 中官方推薦用 event bus 或者 vuex 解決雀久,event bus 的本質(zhì)是一個發(fā)布者訂閱者模式宿稀。
<div id="example">
<Display></Display>
<Increment></Increment>
</div>
var bus = new Vue()
Vue.component('Increment', {
template: `<button @click="increment">+</button>`,
data: function() {
return {count: 0}
},
methods: {
increment: function(){
var increment = this.count++
bus.$emit('inc', increment)
}
}
})
Vue.component('Display', {
template: `<h3>Clicked: {{count}} times</h3>`,
data: function(){
return {count: 0}
},
created: function(){
bus.$on('inc', function(num){
this.count = num
}.bind(this))
}
})
new Vue({
el: "#example",
})
相信大家一看就會懂,這個時候同級組件的溝通需要經(jīng)過父組件bus赖捌,然而在做項(xiàng)目的時候我們有vuex祝沸,就不需要這個了。越庇。罩锐。