不求谌解

不求谌解

💻 Web Dev / Creative 💗 ⚽ 🎧 🏓
twitter
github
jike
email

動手實現一個vue分頁組件

需求分析#

  • 自定義顯示的頁碼按鈕的數量
  • 自定義分頁組件的顏色主題
  • 可以選擇顯示文本內容或者圖標來進行翻頁操作

實現#

  1. 分頁組件需要顯示的頁碼按鈕數量,可以通過 pagesToDisplay () 這個計算屬性定義
  2. 顏色主題可以通過綁定一個paginationClass的 class,返回顏色type實現
  3. 文本或圖標的選擇可以通過嵌套條件判斷 template 實現

分頁原理#

實現分頁主要依靠這兩個參數,total (總條目數),perPage (每頁顯示的條目數量)。後端可以通過這兩個參數,返回相應的數據給前端。
整個分頁組件中,相對來說比較麻煩的地方在於頁碼列表的顯示邏輯。頁碼列表是一個返回 [最小頁碼,最大頁碼] 的數組。進而,問題被切分為如何求解頁碼列表數組中頁碼值的最大值和最小值。這裡定義兩個計算屬性 maxPage (),minPage () 來返回頁碼數組中頁碼值的最大值,和最小值。

頁面組件#

<template>
  <ul class="pagination" :class="paginationClass">
    <!-- 前一頁 -->
    <li
      class="page-item prev-page"
      :class="{ disabled: value === 1,'no-arrows': noArrows }"
    >
      <a class="page-link" arial-label="Previous" @click="prevPage">
      <!-- 是否有文本 -->
        <template v-if="withText">
          perv
        </template>
      <!-- 箭頭圖標 -->
        <i class="arrow-left" v-else></i>
      </a>
    </li>
    <!-- 當前顯示的分頁列表 -->
    <li
      class="page-item"
      v-for="item in range(minPage, maxPage)"
      :key="item"
      :class="{active: value === item}"
    >
      <a class="page-link" @click="changePage(item)">{{item}}</a>
    </li>
    <!-- 後一頁 -->
    <li
      class="paga-item next-page"
      :class="{disabled: value === totalPage, 'no-arrows': noArrows}" 
    >
      <a class="page-link" arial-label="Next" @click="nextPage">
      <!-- 文本內容 -->
        <template v-if="withText">
          Next
        </template>
        <i class="arrow-right" v-else></i>
      </a>
    </li>
  </ul>
</template>

props#

props: {
  // 顏色主題
  type: {
    type: String,
    default: "primary",
    // 驗證`type`的值
    validator: value => {
      return [
        "default",
        "primary",
        "info",
        "success",
        "danger"
      ].includes(value);
    }
  },
  // 是否帶有文本內容
  withText: Boolean,
  // 箭頭圖標
  noArrows: Boolean,
  // 頁碼數
  pageCount: {
    type: Number,
    default: 0
  },
  // 每頁顯示的項數
  perPage: {
    type: Number,
    default: 0
  },
  // 總項數
  total: {
    type: Number,
    default: 0
  },
  // 當前頁碼的值
  value: {
    type: Number,
    default: 1
  }
}

data#

data: {
  // 頁碼列表中顯示的頁碼數量
  defaultPagesToDisplay: 5
}

computed#

computed: {
  // 顏色主題
  paginationClass() {
    return `pagination-${this.type}`
  },
  // 總頁碼數
  toatalPages() {
    if (this.pageCount > 0) return this.pageCount;
    if (this.total > 0 ) {
      return Math.ceil(this.total / this.perPage);
    }
  },
  // 顯示的頁碼按鈕數量
  pagesToDisplay() {
    if (this.totalPages > 0 && this.totalPages < this.defaultPagesToDisplay) {
      return this.totalPages;
    } else {
      return this.defaultPagesToDisplay;
    }
  },
  // 頁碼列表中最小的頁碼數
  minPage() {
    if (this.value >= this.pagesToDisplay) {
      // 定義一個偏移量
      const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
      // 假定的最大頁碼數 = 當前頁碼值 + 偏移量
      const newMaxPage = this.value + pagesToAdd;
      if (newMaxPage > this.totalPages) {
        return totalPages - this.pagesToDisplay + 1;
      } else {
        // 當前頁碼值始位處於頁碼列表的中間位置
        return this.value - pagesToAdd;
      }
    } else {
      // 當前頁碼值小於默認頁碼列表按鈕數量時,最小頁碼數返回1
      return 1;
    }
  },
  // 頁碼列表中最大的頁碼數
  maxPage() {
    if (this.value >= this.pagesToDisplay) {
      const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
      const newMaxPage = this.value + pagesToAdd;
      if (newMaxPage < this.totalPages) {
        return newMaxPage;
      } else {
        return totalPages;
      }
    } else {
      // 當前頁碼值小於默認頁碼列表按鈕數量時,最大頁碼數返回顯示的頁碼列表按鈕數
      return this.pageToDisplay;
    }
  }
}

methods#

methods: {
  // 頁碼列表數組
  range(min, max) {
    let arr =[];
    for (let i = min; i <= max; i++) {
      arr.push(i);
    }
    return arr;
  },
  // 跳轉頁碼
  changePage(item) {
    this.$emit("input", item);
  },
  // 下一頁
  nextPage() {
    if (this.value < this.totalPages) {
      this.$emit("input", this.value + 1);
    }
  },
  // 上一頁
  prevPage() {
    if (this.value > 1) {
      this.$emit("input", this.value - 1);
    }
  }
}

最後#

想要不斷提高自己的技術水平,就不能只滿足於寫業務組件。在寫像分頁這種獨立組件的過程中,也讓我思考API的設計,以及功能複雜性的問題。無論是多麼複雜的組件,大體上都是由 prop,event,slot 三部分組成。具體的實現,主要還是依靠基礎的 javascript 的能力。
此前,我也仿照 element 寫了 radio 和 input 組件。希望自己能堅持去寫這些獨立組件,去發現更大的世界。本文的分頁組件源碼在這兒。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。