vue + element-ui的分頁問題

背景介紹

最近比較空閒,公司的後臺就想着把如今的後臺管理系統給改版一下,說是之前的太難看了,用着也很差用,而後給我甩過來一個ant-design-pro的連接,說是他看這個就挺不錯的。css

我當時內心就想着,以前的那個項目混合在大家的java項目裏,跟普通的jsp頁面差很少,一下就是一大堆的css和js文件,看着我都懼怕(好吧,我認可其實我都不敢看),這能加載的快了就奇了怪了。ant-design最初是爲react設計的,ant-design-pro天然也是用react了,不得不說人家這個界面看着確實舒服。前端

對着ant-design-pro的官方文檔看了一通,貌似看了跟沒看也差很少???算了,仍是直接看代碼吧,整理了一下思路,大體上是看懂了,除了react + react-router外,狀態管理用的是 dva, redux的異步問題算是解決了,要不就開始直接寫頁面吧?vue

等等,我好像漏掉了點什麼?噢,對,先看看打包出來的文件大小,一打包個人心就涼了,最大的js竟然有900多k,ant-design的源文件是真的大。react我還只是能寫出代碼,打包優化這個可就有點爲難我了。這時的我再想到公司那1m的帶寬,還有這幾個後臺的技術能力,要否則這個技術棧我仍是放棄吧?不能期望連 請求頭, CORS稍微高級一點的攜帶cookie, nginx靜態服務器 都搞不懂的人去給我弄個靜態服務器,再順便開啓一下gzip吧?算了算了,找找有沒有vue + element-ui的後臺模板,不用太費勁就找到了vue-element-adminjava

vue-element-admin用着還行,就是界面不太符合個人理想狀況,就對着ant-design-pro改造了一點,列表頁大概就是下面這樣了。列表的數據是要分頁的,普通的列表頁只有一個頁面棧,也就是用戶點擊地址欄的回退地址欄時,會返回上一個頁面棧,而不是上一頁的數據,不太符合用戶習慣吧?畢竟傳統的網站都是能夠回退到上一頁的,嗯,話很少說,進入正題吧。react

列表頁

第一步:改變地址欄

假設列表頁的路徑是 /user/list,分頁相關的參數爲{ page: 1, pagesize: 10 },從其餘頁面跳轉過來的時候,咱們的路徑一般是不包含任何參數的,以後的列表數據都是根據該頁面的page和pagesize進行變化的,當未使用keep-alive緩存組件時,每次進入列表頁都至關於第一次進入,也就是說每次都只能獲取第一頁的數據。nginx

既然列表數據是用page和pagesize進行變化的,那直接從地址欄獲取page和pagesize進行賦值不就行了?那麼是改變地址欄的代碼是直接寫在當前頁面仍是 獨立爲分頁組件 呢?從複用性方面來講,仍是獨立出來的好,畢竟其餘頁面可能也會使用到,總不能每次都複製粘貼吧,那組件化的意義何在?固然了,也不是說分頁就必須用這個自定義的分頁組件,只推薦在主頁面(非遮罩層,有的頁面會在點擊某一行數據時出現遮罩層顯示子列表,此時使用element-ui的分頁組件便可)須要分頁時使用。git

當改變地址欄的時候,咱們是不但願不帶分頁參數的頁面棧存在的,此時用replace直接替換便可。github

MyPagination.vue的初始結構爲:element-ui

<template>
  <div class = " flex all-center">
    <template v-if="total > 0">
      <el-pagination
        :page-size="pagesize"
        :total="total"
        :current-page="page"
        background
        layout="prev, pager, next, jumper, total"
        class="my-pagination"
        @current-change="changePage" />
    </template>
  </div>
</template>

<script>
export default {
  name: 'MyPagination',
  props: {
    total: {
      type: Number,
      default: 0,
    },
    page: {
      type: Number,
      default: 1,
    },
    pagesize: {
      type: Number,
      default: 10,
    },
    totalPages: {
      type: Number,
      default: 1,
    },
  },
  created() {
    this.getCurrentPage();
  },
  methods: {
    changePage(val) {
      this.handlePage('push', val, this.pagesize);
      this.$emit('change', val, this.pagesize);
    },
    getCurrentPage() {
      var { page, pagesize } = this.$route.query;
      if (!page || !pagesize) {
        this.handlePage('replace', page || 1, +pagesize || this.pagesize);
        return true;
      }
      return false;
    },
    handlePage(type, page, pagesize) {
      this.$router[type]({
        path: this.$route.path,
        query: { ...this.$route.query, page, pagesize },
      });
    }
  },
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.my-pagination {
  padding-top: 24px;   
}
</style>

複製代碼

父組件的關鍵代碼:redux

<MyPagination :total = "total" :pagesize = "pagesize" :page="page" :totalPages = "totalPages" @change = "changePage" />

methods: {
    changePage(page, pagesize) {
       var _page = this.page,
           _pagesize = this.pagesize;
      this.page = page;
      this.pagesize = pagesize;
      if (page !== _page && pagesize || _pagesize !== pagesize) this.fetchData(); // 非首次進入頁面時再獲取分頁數據,由於在created鉤子中已經獲取過一次了。
    },
}
複製代碼

實現效果: 首次進入該頁面時,若是不含有分頁參數,就會先改變分頁參數,而後再獲取數據,以後點擊分頁組件的頁碼也會獲取分頁以後的數據。

第二步: 觀察路由變化

上一步的實現效果乍一看好像沒什麼不對勁的地方,可是若是直接改變地址欄的話,顯示的當前頁和當前數據都不會變化。前端路由在頁面的查詢參數(指的是router的查詢參數,可不是普通頁面的查詢參數)變化時,默認是不會從新加載的,除非頁面的key發生變化,這樣是爲了儘量的防止頁面從新渲染,因此就不用key的方式解決了,直接經過vue的watch檢測 $route 的變化,從而改變當前頁和當前數據的顯示問題。

在MyPagination.vue中新增:

watch: {
    '$route'(to, from) {
      let { page, pagesize } = to.query;
      if (!this.getCurrentPage()) {
        this.$emit('change', +page || 1, +pagesize || 10);
      }
    }
},
複製代碼

第三步: 控制pagesize的大小

在上一步的效果中,當改變地址欄的page和pagesize時,列表頁的數據也會隨之變化。既然是根據地址欄的參數變化,那麼新的問題就產生了,

若是用戶輸入的page大於頁面總數呢?

這個時候主要就看後臺怎麼設計了,

  1. 返回第一頁的數據。
getCurrentPage() {
  var { page, pagesize } = this.$route.query;
  /* 
  (totalPages > 0 && (page > totalPages));知足總頁數大於0且當前頁大於總頁數時,跳轉到第一頁
  */
  if (!page || !pagesize || (totalPages > 0 && (page > totalPages))) {
    this.handlePage('replace', page || 1, this.pagesize);
    return true;
  }
  return false;
},
複製代碼
  1. 返回最後一頁的數據(我以爲這種操做應該是比較合理的)。
getCurrentPage() {
  var { page, pagesize } = this.$route.query,
      MAX_PAGESIZE = this.max,
      totalPages = this.totalPages;
  if (!page || !pagesize) {
    this.handlePage('replace', page || 1, +pagesize || this.pagesize);
    return true;
  } else if (totalPages > 0 && (page > totalPages)) {
    this.handlePage('replace', totalPages, +pagesize);
    return true;
  }
  return false;
},
複製代碼

替換當前頁面棧,return true的做用是阻止watch中的後續操做,取消本次請求。替換頁面之後,請求遠程數據,更新當前頁和數據的顯示。

  1. 返回空數組(可能大多數後臺都是這麼設計的,他們應該沒想過page會大於總頁數吧)。 代碼與2中的同樣。

上文都是創建在totalPages已肯定的狀況,若是是首次進入頁面的話狀況就會不同了。

若是是首次進入頁面的話,totalPages第一次是0,也就是地址欄的參數將不會發生變化,這時候就會出現地址欄和分頁組件的顯示不一致的狀況。這時候能夠在分頁組件中watch totalPages的變化。

totalPages(newVal, oldVal) {
  if (+oldVal === 0 && newVal > 0) {
    this.handlePage('replace', this.page, +this.pagesize);
  }
}
複製代碼

若是pagesize過大呢?

pagesize是必需要進行限制的,若是太大的話,後臺查詢數據就會很是慢,也可能會形成壓力。 解決辦法其實也簡單,就是在props增長一個max屬性,而後在getCurrentPage方法中進行限制,代碼以下:

props: {
    max: {
      type: Number,
      default: 20,
    },
},
methods: {
    getCurrentPage() {
      var { page, pagesize } = this.$route.query,
          MAX_PAGESIZE = this.max,
          totalPages = this.totalPages;
      if (!page || !pagesize) {
        this.handlePage('replace', page || 1, +pagesize || this.pagesize);
        return true;
      } else if (pagesize > MAX_PAGESIZE) {
        this.handlePage('replace', page, MAX_PAGESIZE);
        return true;
      } else if (totalPages > 0 && (page > totalPages)) {
        this.handlePage('replace', totalPages, +pagesize);
        return true;
      }
      return false;
    },
},
複製代碼

第四步: 優化代碼

點擊分頁組件的頁碼時產生兩次請求

點擊分頁組件時,1. 會監聽current-change事件並改變地址欄,同時emit change事件至父組件,2. 可是地址欄改變後,在watch $route也會emit change事件至父組件,那麼只須要合併emit change事件,即current-change事件中只改變地址欄。

changePage(val) {
  this.handlePage('push', val, this.pagesize);
},
複製代碼

結果

至此,一個自定義的分頁組件就已經實現了,改變地址欄的參數就能夠看到分頁數據的變化了,點擊頁碼時地址欄也會隨之而改變,請求數量已經儘量的減小了。

自定義的分頁組件: MyPagination.vue

列表頁: list.vue

完整demo: front_end

若是有什麼問題,歡迎你們留言進行探討。

相關文章
相關標籤/搜索