分页功能实现步骤

64 阅读8分钟

Vue.js 分页功能实现详解


一、分页功能概述

1.1 什么是分页?

分页就是将大量数据分成多个页面显示,每页只显示部分数据。就像看书一样,一页一页地翻看内容。

举个例子:

  • 假设你有 100 条数据
  • 每页显示 10 条
  • 那么就需要 10 页来显示所有数据
  • 第 1 页显示:第 1-10 条
  • 第 2 页显示:第 11-20 条
  • 以此类推...

1.2 为什么需要分页?

  1. 性能优化:一次性加载所有数据会导致页面卡顿
  2. 用户体验:用户不需要滚动很久才能找到内容
  3. 网络优化:减少数据传输量

二、核心概念理解

2.1 分页的三个核心变量

current - 当前页码
current: 1  // 表示当前在第几页,从 1 开始
  • 作用:记录用户当前查看的是第几页
  • 初始值:通常设置为 1(第一页)
  • 变化时机:用户点击分页组件切换页码时
pageSize - 每页显示条数
pageSize: 10  // 表示每页显示多少条数据
  • 作用:控制每页显示的数据量
  • 常见值:10、20、50、100
  • 变化时机:用户通过下拉菜单选择不同的每页条数时
total - 数据总数
total: 100  // 表示总共有多少条数据
  • 作用:告诉分页组件总共有多少条数据,用于计算总页数
  • 计算方式:通常是数组的长度 array.length
  • 变化时机:数据加载或筛选后自动更新

2.2 分页计算公式

核心公式:如何计算要显示哪些数据?

假设:

  • 总数据:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ...] (共 25 条)
  • 当前页码:current = 2
  • 每页条数:pageSize = 10

计算步骤:

  1. 计算起始索引(start)

    start = (current - 1) * pageSize
    start = (2 - 1) * 10 = 10
    
    • 解释:第 2 页要从第 10 条开始(因为第 1 页已经显示了 0-9 条)
  2. 计算结束索引(end)

    end = start + pageSize
    end = 10 + 10 = 20
    
    • 解释:第 2 页显示到第 20 条(不包含第 20 条,因为数组索引从 0 开始)
  3. 切片获取数据

    paginatedList = list.slice(start, end)
    paginatedList = list.slice(10, 20)  // 获取索引 10-19 的数据
    // 结果:[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
    

可视化理解:

总数据:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
索引:  [0, 1, 2, 3, 4, 5, 6, 7, 8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]

第 1 页 (current=1, pageSize=10):
  start = (1-1) * 10 = 0
  end = 0 + 10 = 10
  slice(0, 10) → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

第 2 页 (current=2, pageSize=10):
  start = (2-1) * 10 = 10
  end = 10 + 10 = 20
  slice(10, 20) → [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

第 3 页 (current=3, pageSize=10):
  start = (3-1) * 10 = 20
  end = 20 + 10 = 30
  slice(20, 30) → [21, 22, 23, 24, 25]  // 只有 5 条,因为总共只有 25 条

三、实现步骤详解

步骤 1:在 data 中定义分页变量

位置data() 函数中

data() {
  return {
    // ... 其他数据
    // 分页相关
    current: 1,      // 当前页码,从 1 开始
    pageSize: 10,    // 每页显示条数
  }
}

详细说明:

  • current: 1:初始化时显示第一页
  • pageSize: 10:默认每页显示 10 条,可以根据需求调整

为什么从 1 开始?

  • 用户习惯:用户看到的是"第 1 页"、"第 2 页",而不是"第 0 页"
  • 但在计算数组索引时,需要转换为从 0 开始(通过 current - 1

步骤 2:创建计算属性获取当前页数据

位置computed 对象中

computed: {
  // 获取当前 tab 的所有数据
  currentList() {
    const map = {
      ch: this.ch,
      jlwyq: this.jlwyq,
      jlyq: this.jlyq,
      db: this.db,
      dbyq: this.dbyq,
      qbhs: this.qbhs
    }
    return map[this.activeTab] || []
  },
  
  // 分页后的列表(核心逻辑)
  paginatedList() {
    const list = this.currentList  // 获取当前 tab 的所有数据
    const start = (this.current - 1) * this.pageSize  // 计算起始索引
    const end = start + this.pageSize  // 计算结束索引
    return list.slice(start, end)  // 切片获取当前页的数据
  },
  
  // 分页总数
  total() {
    return this.currentList.length  // 返回当前 tab 的数据总数
  }
}

逐行解析 paginatedList

paginatedList() {
  // 第 1 步:获取完整的数据列表
  const list = this.currentList
  // 例如:list = [1, 2, 3, ..., 100] (100 条数据)
  
  // 第 2 步:计算起始索引
  const start = (this.current - 1) * this.pageSize
  // 假设 current = 2, pageSize = 10
  // start = (2 - 1) * 10 = 10
  // 意思:第 2 页从索引 10 开始(即第 11 条数据)
  
  // 第 3 步:计算结束索引
  const end = start + this.pageSize
  // end = 10 + 10 = 20
  // 意思:第 2 页到索引 20 结束(不包含 20,即到第 20 条数据)
  
  // 第 4 步:使用 slice 方法切片
  return list.slice(start, end)
  // list.slice(10, 20) 返回索引 10-19 的数据
  // 结果:[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
}

为什么使用计算属性?

  • 自动更新:当 currentpageSizecurrentList 变化时,paginatedList 会自动重新计算
  • 性能优化:Vue 会缓存计算结果,只有依赖变化时才重新计算
  • 代码简洁:不需要手动调用函数更新数据

步骤 3:修改模板使用分页后的数据

位置<template>

修改前:

<div class="list-card" v-for="(item, index) in currentList" :key="index">
  <!-- 卡片内容 -->
</div>

修改后:

<div class="list-card" v-for="(item, index) in paginatedList" :key="index">
  <!-- 卡片内容 -->
</div>

关键变化:

  • currentListpaginatedList
  • 现在循环的是分页后的数据,而不是全部数据

工作原理:

  1. 用户看到的是 paginatedList(当前页的数据)
  2. 当用户切换页码时,current 改变
  3. paginatedList 自动重新计算
  4. 页面自动更新显示新的数据

步骤 4:添加分页组件

位置:列表下方

<!-- 分页 -->
<div class="paging" v-if="total > 0">
  <a-pagination 
    v-model="current" 
    :total="total" 
    :page-size="pageSize"
    :show-total="(total) => `共 ${total} 条`"
    show-size-changer
    :page-size-options="['10', '20', '50', '100']"
    @change="handlePageChange"
    @showSizeChange="handlePageSizeChange"
  />
</div>

属性详解:

属性说明示例值
v-model="current"双向绑定当前页码1, 2, 3...
:total="total"数据总数100
:page-size="pageSize"每页显示条数10
:show-total显示总条数的函数共 100 条
show-size-changer显示每页条数选择器true
:page-size-options每页条数选项['10', '20', '50', '100']
@change页码变化事件用户点击页码时触发
@showSizeChange每页条数变化事件用户选择每页条数时触发

v-if="total > 0" 的作用:

  • 只有当有数据时才显示分页组件
  • 如果没有数据,显示分页组件没有意义

步骤 5:实现分页事件处理函数

位置methods 对象中

methods: {
  // 页码变化处理
  handlePageChange(page) {
    this.current = page  // 更新当前页码
    // 滚动到顶部(可选,提升用户体验)
    const contentEl = document.querySelector('.content')
    if (contentEl) {
      contentEl.scrollTop = 0
    }
  },
  
  // 每页条数变化处理
  handlePageSizeChange(current, size) {
    this.pageSize = size  // 更新每页条数
    this.current = 1     // 重置到第一页(重要!)
  }
}

handlePageChange 详解:

handlePageChange(page) {
  // page 参数:用户点击的新页码,例如点击第 3 页,page = 3
  
  // 更新当前页码
  this.current = page
  // 此时 current 从 2 变成 3
  // paginatedList 会自动重新计算,显示第 3 页的数据
  
  // 可选:滚动到顶部,让用户看到新页面的内容
  const contentEl = document.querySelector('.content')
  if (contentEl) {
    contentEl.scrollTop = 0
  }
}

handlePageSizeChange 详解:

handlePageSizeChange(current, size) {
  // current: 当前页码(通常可以忽略)
  // size: 用户选择的新每页条数,例如选择 20,size = 20
  
  // 更新每页条数
  this.pageSize = size
  // 此时 pageSize 从 10 变成 20
  // paginatedList 会自动重新计算,每页显示 20 条
  
  // 重要:重置到第一页
  this.current = 1
  // 为什么?因为如果用户在第 3 页,然后改成每页 20 条
  // 第 3 页可能没有数据了(总共只有 2 页)
  // 所以重置到第 1 页更安全
}

步骤 6:在关键操作时重置页码

位置:切换 tab、搜索、重置等方法中

// 切换 tab 时重置页码
selectTab(key) {
  this.activeTab = key
  this.current = 1  // 重置到第一页
}

// 搜索时重置页码
search() {
  this.current = 1  // 重置到第一页
  this.getUserList()
}

// 重置条件时重置页码
reset() {
  this.keyword = ''
  this.unit = ''
  this.casePolice = ''
  this.current = 1  // 重置到第一页
  this.getUserList()
}

四、代码逐行解析

4.1 完整代码结构

<template>
  <!-- 列表显示 -->
  <div class="content">
    <div v-for="(item, index) in paginatedList" :key="index">
      <!-- 卡片内容 -->
    </div>
  </div>
  
  <!-- 分页组件 -->
  <div class="paging" v-if="total > 0">
    <a-pagination 
      v-model="current" 
      :total="total" 
      :page-size="pageSize"
      @change="handlePageChange"
      @showSizeChange="handlePageSizeChange"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 原始数据
      ch: [],
      jlwyq: [],
      // ... 其他数据
      
      // 分页变量
      current: 1,      // 当前页码
      pageSize: 10,    // 每页条数
    }
  },
  
  computed: {
    // 获取当前 tab 的所有数据
    currentList() {
      // ... 根据 activeTab 返回对应数组
    },
    
    // 分页后的数据(核心)
    paginatedList() {
      const list = this.currentList
      const start = (this.current - 1) * this.pageSize
      const end = start + this.pageSize
      return list.slice(start, end)
    },
    
    // 数据总数
    total() {
      return this.currentList.length
    }
  },
  
  methods: {
    // 页码变化
    handlePageChange(page) {
      this.current = page
    },
    
    // 每页条数变化
    handlePageSizeChange(current, size) {
      this.pageSize = size
      this.current = 1
    },
    
    // 切换 tab(重置页码)
    selectTab(key) {
      this.activeTab = key
      this.current = 1
    }
  }
}
</script>

4.2 数据流向图

用户操作
   ↓
触发事件(点击页码/切换tab/搜索)
   ↓
更新 data 中的变量(current, pageSize, activeTab)
   ↓
触发 computed 重新计算
   ↓
paginatedList 自动更新
   ↓
模板自动重新渲染
   ↓
用户看到新的页面内容

记住用户的每页条数偏好

// 使用 localStorage 保存用户选择
handlePageSizeChange(current, size) {
  this.pageSize = size
  this.current = 1
  // 保存到本地存储
  localStorage.setItem('pageSize', size)
}

// 组件创建时读取
created() {
  const savedPageSize = localStorage.getItem('pageSize')
  if (savedPageSize) {
    this.pageSize = parseInt(savedPageSize)
  }
}

分页功能的核心要点

  1. 三个变量current(当前页)、pageSize(每页条数)、total(总数)
  2. 一个公式start = (current - 1) * pageSizeend = start + pageSize
  3. 一个方法list.slice(start, end) 获取当前页数据
  4. 计算属性:使用 computed 自动响应数据变化
  5. 事件处理:处理页码和每页条数变化
  6. 重置逻辑:关键操作时重置到第 1 页

组件核心

<a-pagination
  :current="current"
  :page-size="pageSize"
  :total="total"
/>