Vue手写分页器

本文介绍如何封装一个Vue分页器组件,详细阐述了分页需求的分析,包括计算连续页码范围、渲染逻辑,并提供了完整的源码实现。通过组件化实现,提升前端页面加载效率,改善用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何封装一个分页器组件

文章最后会提供源码

分析与代码部分

需要分页器的原因:从服务器中获取的数据很多,如果全部一次性加载会导致卡顿,用户体验差

我们写一个分页器需要的外部数据(即使用该分页器组件时需要通过props传入的数据):

  1. 当前是第几页 pageNo

  2. 每一页展示多少数据 pageSize

  3. 整个分页器一共有多少数据 total

  4. 分页器连续页码个数 continues(通常是奇数5 / 7,一般要求对称)

ps:其中暗含一个隐藏数据:整个分页器一共有多少页 Math.ceil(total / pageSize)




下面开始写代码:

计算连续页码的起始数字与结束数字

连续页码就是分页器中按数字顺序连续显示的页码,个数为 continues

在这里插入图片描述
例如上图中的 3、4、5、6、7 这5个页码,我们要先计算出这几个连续页码的开始页码和结束页码

逻辑:当前页码为第n页,假设continues = 5,那么起始数字为n-2,结束数字为n+2

// computed中
totalPage() {
	return Math.ceil(this.total / this.pageSize);
},
// 连续页码起始数字与结束数字
startNumAndEndNum() {
    const { pageNo, totalPage, continues } = this;
    let start = 0;
    let end = 0;
    // 如果总页数少于连续页码数,只展示 1~最后页码数 即可
    if (totalPage < continues) {
        start = 1;
        end = totalPage;
    // 如果总页数大于等于连续页码数
    } else {
        start = pageNo - Math.floor(continues / 2);
        end = pageNo + Math.floor(continues / 2);
        // start为 0 / 负数
        if (start < 1) {
            start = 1;
            end = continues;
        }
        // end大于总页数
        if (end > totalPage) {
            start = totalPage - continues + 1;
            end = totalPage;
    	}
}
	return {start, end};
},
连续页码数组
// computed中
pagesArr() {
    let arr = [];
    let pageNum = this.startNumAndEndNum.start;
    arr.length = (this.totalPage < this.continues ? this.totalPage : this.continues);
    for (let i = 0; i < arr.length; i++) {
        arr[i] = pageNum;
        pageNum++;
    }
    return arr
}
渲染数据
<ul>
    <li class="prev disabled">
        <a href="#">« 上一页</a>
    </li>
    <li v-show="startNumAndEndNum.start > 1">
        <a href="#">1</a>
    </li>
    <li class="dotted" v-show="startNumAndEndNum.start > 2"><span>...</span></li>

    <!-- 中间部分 -->
    <li v-for="(p, index) in pagesArr" :key="index">
        <a href="#">{{p}}</a>
    </li>

    <li class="dotted" v-show="startNumAndEndNum.end < totalPage - 1"><span>...</span></li>
    <li v-show="startNumAndEndNum.end < totalPage">
        <a href="#">{{ totalPage }}</a>
    </li>
    <li class="next">
        <a href="#">下一页 »</a>
    </li>
</ul>

完整代码:

<template>
  <div class="fr page">
    <div class="sui-pagination clearfix">
      <ul>
        <li
          class="prev"
          :class="{ disabled: pageNo === 1 }"
          @click="pageNo > 1 ? $emit('getPageNo', pageNo - 1) : null"
        >
          <a>« 上一页</a>
        </li>
        <li
          v-show="startNumAndEndNum.start > 1"
          :class="{ active: pageNo === 1 }"
          @click="$emit('getPageNo', 1)"
        >
          <a>1</a>
        </li>
        <li class="dotted" v-show="startNumAndEndNum.start > 2">
          <span>...</span>
        </li>

        <!-- 中间部分 -->
        <li
          v-for="(page, index) in pagesArr"
          :key="index"
          :class="{ active: pageNo === page }"
          @click="$emit('getPageNo', page)"
        >
          <a>{{ page }}</a>
        </li>

        <li class="dotted" v-show="startNumAndEndNum.end < totalPage - 1">
          <span>...</span>
        </li>
        <li
          v-show="startNumAndEndNum.end < totalPage"
          :class="{ active: pageNo === totalPage }"
          @click="$emit('getPageNo', totalPage)"
        >
          <a>{{ totalPage }}</a>
        </li>
        <li
          class="next"
          :class="{ disabled: pageNo === totalPage }"
          @click="pageNo < totalPage ? $emit('getPageNo', pageNo + 1) : null"
        >
          <a>下一页 »</a>
        </li>
      </ul>
      <div>
        <span>{{ total }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "Pagination",
  props: ["pageNo", "pageSize", "total", "continues"],
  computed: {
    totalPage() {
      return Math.ceil(this.total / this.pageSize);
    },
    // 连续页码起始数字与结束数字
    startNumAndEndNum() {
      const { pageNo, continues, totalPage } = this;
      let start = 0;
      let end = 0;
      // 如果总页数少于连续页数
      if (totalPage < continues) {
        start = 1;
        end = totalPage;
      } else {
        start = pageNo - Math.floor(continues / 2);
        end = pageNo + Math.floor(continues / 2);
        // start出现0/负数
        if (start < 1) {
          start = 1;
          end = continues;
        }
        // end大于总页数
        if (end > totalPage) {
          start = totalPage - continues + 1;
          end = totalPage;
        }
      }
      return { start, end };
    },
    pagesArr() {
      let arr = [];
      // 连续页码个数
      arr.length =
        this.totalPage < this.continues ? this.totalPage : this.continues;
      // 第一个连续页码
      let pageNum = this.startNumAndEndNum.start;
      for (let i = 0; i < arr.length; i++) {
        arr[i] = pageNum;
        pageNum++;
      }
      return arr;
    },
  },
};
</script>

<style scoped lang="less">
.page {
  display: flex;
  justify-content: center;
  overflow: hidden;

  .sui-pagination {
    margin: 18px 0;

    ul {
      margin-left: 0;
      margin-bottom: 0;
      vertical-align: middle;
      float: left;

      li {
        line-height: 18px;
        display: inline-block;

        a {
          position: relative;
          float: left;
          background-color: #fff;
          padding: 9px 18px;
          border: 1px solid #e0e9ee;
          margin-left: -1px;
          line-height: 18px;
          text-decoration: none;
          font-size: 14px;
          color: #333;
          cursor: pointer;
        }

        &.active {
          a {
            background-color: #e1251b;
            color: #fff;
            cursor: default;
          }
        }

        &.prev {
          a {
            margin-left: 0px;
            background-color: #fafafa;
          }
        }

        &.disabled {
          a {
            color: #999;
            cursor: default;
          }
        }

        &.dotted {
          span {
            position: relative;
            float: left;
            padding: 9px 18px;
            border: 1px solid #e0e9ee;
            margin-left: -1px;
            background-color: #fff;
            font-size: 14px;
            line-height: 18px;
            text-decoration: none;
            color: #333;
          }
        }

        &.next {
          a {
            background-color: #fafafa;
          }
        }
      }
    }

    div {
      margin-top: 10px;
      margin-left: 20px;
      color: #333;
      font-size: 14px;
      float: left;
      width: 80px;
    }
  }
}
</style>

在其他组件中使用:

<!-- 分页器 -->
<Pagination
	// 引号内写入想传入的数据
	:pageNo=""
	:pageSize=""
	:total=""
	:continues=""
	@getPageNo=""
/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值