Vue2自定義分頁組件

分頁是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ù)施绎。我們來分析一下:

  1. 中間顯示的條目是5,左邊的 ... 在當(dāng)前頁面大于3時顯示贞绳,這個很好判斷谷醉。
  2. 中間顯示的條目是5,右邊的 ... 在當(dāng)前頁面小于18時顯示冈闭,這個很好判斷俱尼。
  3. 當(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}&currentPage=${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祝沸,就不需要這個了。越庇。罩锐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市卤唉,隨后出現(xiàn)的幾起案子涩惑,更是在濱河造成了極大的恐慌,老刑警劉巖桑驱,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竭恬,死亡現(xiàn)場離奇詭異,居然都是意外死亡熬的,警方通過查閱死者的電腦和手機(jī)痊硕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來押框,“玉大人岔绸,你說我怎么就攤上這事。” “怎么了亭螟?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵挡鞍,是天一觀的道長。 經(jīng)常有香客問我预烙,道長墨微,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任扁掸,我火速辦了婚禮翘县,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谴分。我一直安慰自己锈麸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布牺蹄。 她就那樣靜靜地躺著忘伞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沙兰。 梳的紋絲不亂的頭發(fā)上氓奈,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機(jī)與錄音鼎天,去河邊找鬼舀奶。 笑死,一個胖子當(dāng)著我的面吹牛斋射,可吹牛的內(nèi)容都是我干的育勺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼罗岖,長吁一口氣:“原來是場噩夢啊……” “哼涧至!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桑包,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤化借,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捡多,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铐炫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年垒手,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倒信。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡科贬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榜掌,我是刑警寧澤优妙,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站憎账,受9級特大地震影響套硼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胞皱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一邪意、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧反砌,春花似錦雾鬼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酒贬,卻和暖如春又憨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背同衣。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工竟块, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人耐齐。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓浪秘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親埠况。 傳聞我的和親對象是個殘疾皇子耸携,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容