1. 项目概述:Ruoyi+Vue2分页方案实战
最近在重构一个后台管理系统时,遇到了数据分页展示的需求。作为国内广泛使用的快速开发框架,Ruoyi+Vue2的组合提供了三种典型的分页实现方式:不分页、前端分页和后端分页。这三种方案各有适用场景,我在实际项目中都尝试过,今天就来分享一下我的踩坑经验和最佳实践。
这个Demo特别适合刚接触Ruoyi框架的前端开发者,尤其是需要处理大量数据展示的场景。通过对比不同分页方式的实现细节,你能快速掌握Vue2在Ruoyi框架下的数据交互模式。下面我会从原理到代码,详细拆解每种方案的实现要点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与基础配置
2.1 Ruoyi-Vue项目初始化
首先确保你已经正确搭建了Ruoyi-Vue开发环境。我推荐使用以下版本组合:
- JDK 1.8
- Node.js 14.x
- Vue 2.6.11
- Element UI 2.15.6
在src/views/demo目录下新建我们的演示页面:
bash复制mkdir -p src/views/demo/pagination
touch src/views/demo/pagination/index.vue
2.2 基础数据模拟
为了方便演示,我们在本地mock三种分页方式需要的数据。在src/api/demo/pagination.js中定义API接口:
javascript复制import request from '@/utils/request'
// 获取不分页数据
export function getFullList() {
return request({
url: '/demo/pagination/full',
method: 'get'
})
}
// 获取前端分页数据
export function getFrontendList() {
return request({
url: '/demo/pagination/frontend',
method: 'get'
})
}
// 获取后端分页数据
export function getBackendList(query) {
return request({
url: '/demo/pagination/backend',
method: 'get',
params: query
})
}
3. 不分页方案实现
3.1 适用场景分析
不分页方案最适合数据量小(通常<100条)、需要完整展示所有数据的场景。比如系统配置项、下拉选项等。它的优势是:
- 一次性加载,无需额外分页逻辑
- 响应速度快(数据量小时)
- 实现简单直接
3.2 核心代码实现
vue复制<template>
<div class="app-container">
<el-table
:data="tableData"
border
style="width: 100%">
<el-table-column
prop="id"
label="ID"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="名称">
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getFullList } from '@/api/demo/pagination'
export default {
data() {
return {
tableData: []
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
getFullList().then(response => {
this.tableData = response.data
})
}
}
}
</script>
3.3 注意事项与优化
重要提示:当数据量超过500条时,不建议使用此方案,会导致以下问题:
- 首屏加载时间过长
- 内存占用高
- 渲染性能下降
优化建议:
- 添加loading状态提示
- 实现错误处理机制
- 对于超大数据集考虑虚拟滚动方案
4. 前端分页方案实现
4.1 适用场景分析
前端分页适合中等数据量(100-1000条)、需要快速响应分页操作的场景。典型特点:
- 一次性加载所有数据
- 分页计算在前端完成
- 适合数据变化不频繁的场景
4.2 核心代码实现
vue复制<template>
<div class="app-container">
<el-table
:data="pagedData"
border
style="width: 100%">
<!-- 列定义同上 -->
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</template>
<script>
import { getFrontendList } from '@/api/demo/pagination'
export default {
data() {
return {
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0
}
},
computed: {
pagedData() {
return this.tableData.slice(
(this.currentPage - 1) * this.pageSize,
this.currentPage * this.pageSize
)
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
getFrontendList().then(response => {
this.tableData = response.data
this.total = response.data.length
})
},
handleSizeChange(val) {
this.pageSize = val
},
handleCurrentChange(val) {
this.currentPage = val
}
}
}
</script>
4.3 性能优化技巧
- 内存管理:对于超大数组,考虑使用
Object.freeze()防止Vue响应式劫持 - 计算属性缓存:确保
pagedData只在实际依赖变化时重新计算 - 分页器配置:合理设置
page-sizes选项,避免单页数据过多
5. 后端分页方案实现
5.1 适用场景分析
后端分页是处理大数据量(>1000条)的标准方案,具有以下优势:
- 数据传输量小
- 服务端压力可控
- 支持复杂查询条件
- 数据实时性高
5.2 核心代码实现
vue复制<template>
<div class="app-container">
<el-table
v-loading="loading"
:data="tableData"
border
style="width: 100%">
<!-- 列定义同上 -->
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryParams.pageNum"
:page-sizes="[10, 20, 50, 100]"
:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</template>
<script>
import { getBackendList } from '@/api/demo/pagination'
export default {
data() {
return {
loading: false,
tableData: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10
}
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.loading = true
getBackendList(this.queryParams).then(response => {
this.tableData = response.rows
this.total = response.total
this.loading = false
}).catch(() => {
this.loading = false
})
},
handleSizeChange(val) {
this.queryParams.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryParams.pageNum = val
this.fetchData()
}
}
}
</script>
5.3 后端接口规范
Ruoyi框架推荐的后端分页响应格式:
json复制{
"code": 200,
"msg": "success",
"rows": [...], // 当前页数据
"total": 100 // 总记录数
}
对应的MyBatis分页查询SQL示例:
xml复制<select id="selectList" parameterType="Map" resultMap="BaseResultMap">
select * from sys_demo
<where>
<if test="name != null and name != ''">
AND name like concat('%', #{name}, '%')
</if>
</where>
order by create_time desc
limit #{offset}, #{pageSize}
</select>
6. 三种方案对比与选型建议
6.1 性能对比表
| 指标 | 不分页 | 前端分页 | 后端分页 |
|---|---|---|---|
| 首次加载时间 | 长 | 中等 | 短 |
| 翻页响应速度 | 无 | 快 | 中等 |
| 服务器压力 | 高 | 中等 | 低 |
| 内存占用 | 高 | 中等 | 低 |
| 数据实时性 | 差 | 差 | 好 |
6.2 选型决策树
- 数据量 < 100条 → 不分页
- 100 ≤ 数据量 ≤ 1000 且 数据变化不频繁 → 前端分页
- 数据量 > 1000 或 需要实时数据 → 后端分页
- 不确定数据量大小 → 默认使用后端分页
6.3 特殊场景处理
场景1:带条件查询的分页
- 必须使用后端分页
- 注意重置页码到第一页
javascript复制handleSearch() {
this.queryParams.pageNum = 1
this.fetchData()
}
场景2:TreeTable分页
- 每层节点单独分页
- 需要维护展开状态
- 推荐使用前端分页+懒加载
7. 常见问题与解决方案
7.1 分页器常见问题
问题1:分页器显示异常
- 症状:总页数计算错误
- 原因:未正确设置
total属性 - 修复:确保从接口获取正确的total值
问题2:切换分页大小时表格跳动
- 解决方案:为表格添加固定高度
css复制.el-table {
height: calc(100vh - 300px);
}
7.2 数据同步问题
问题:新增数据后分页状态不一致
- 解决方案:
javascript复制// 新增成功后重置查询
handleAddSuccess() {
this.queryParams.pageNum = 1
this.fetchData()
}
7.3 性能优化技巧
- 防抖处理:对频繁触发的分页操作添加防抖
javascript复制import { debounce } from 'lodash'
methods: {
handleCurrentChange: debounce(function(val) {
this.queryParams.pageNum = val
this.fetchData()
}, 300)
}
- 缓存策略:对静态数据使用localStorage缓存
javascript复制fetchData() {
const cacheKey = `pagination_${JSON.stringify(this.queryParams)}`
const cachedData = localStorage.getItem(cacheKey)
if (cachedData) {
const { data, expire } = JSON.parse(cachedData)
if (expire > Date.now()) {
this.tableData = data.rows
this.total = data.total
return
}
}
// 正常请求接口...
}
8. 高级应用:分页组件封装
8.1 可复用分页组件
创建src/components/Pagination/index.vue:
vue复制<template>
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</template>
<script>
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
}
}
}
</script>
8.2 在父组件中使用
vue复制<template>
<div>
<!-- 表格内容 -->
<pagination
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="fetchData"
/>
</div>
</template>
<script>
import Pagination from '@/components/Pagination'
export default {
components: { Pagination },
// 其他逻辑保持不变
}
</script>
9. 测试与调试技巧
9.1 分页边界测试用例
- 测试第一页和最后一页
- 测试每页显示条数切换
- 测试在空数据集时的分页器表现
- 测试带条件查询时的分页重置
9.2 Vue DevTools调试技巧
- 监控分页相关数据变化
- 检查计算属性
pagedData的更新时机 - 跟踪分页事件触发顺序
9.3 性能分析工具
使用Chrome Performance工具分析:
- 前端分页的JS执行时间
- 表格渲染耗时
- 内存占用情况
10. 项目部署与优化
10.1 生产环境配置
在vue.config.js中添加分页相关的优化配置:
javascript复制module.exports = {
chainWebpack: config => {
// 启用预加载
config.plugin('preload').tap(options => {
options[0].include = 'all'
return options
})
// 分页相关组件单独打包
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
pagination: {
name: 'chunk-pagination',
test: /[\\/]src[\\/]components[\\/]Pagination/,
priority: 20
}
}
})
}
}
10.2 Nginx优化配置
对于后端分页接口,添加缓存策略:
nginx复制location /api/demo/pagination {
proxy_cache pagination_cache;
proxy_cache_valid 200 5m;
proxy_cache_key "$scheme$request_method$host$request_uri";
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
}
11. 扩展思考:分页模式创新
11.1 无限滚动分页
结合v-infinite-scroll指令实现:
vue复制<template>
<div
v-infinite-scroll="loadMore"
infinite-scroll-disabled="busy"
infinite-scroll-distance="100">
<!-- 数据列表 -->
</div>
</template>
<script>
export default {
methods: {
loadMore() {
this.busy = true
this.queryParams.pageNum++
getBackendList(this.queryParams).then(response => {
this.tableData = this.tableData.concat(response.rows)
this.busy = false
})
}
}
}
</script>
11.2 虚拟滚动分页
使用vue-virtual-scroller处理超大数据集:
vue复制<template>
<RecycleScroller
class="scroller"
:items="tableData"
:item-size="54"
key-field="id">
<template v-slot="{ item }">
<!-- 渲染每行数据 -->
</template>
</RecycleScroller>
</template>
12. 项目总结与经验分享
在实际项目中,我总结了以下几点经验:
-
分页策略选择:不要过早优化,初期可以使用后端分页作为默认方案,后续根据实际数据量和性能需求调整
-
分页参数设计:保持统一的参数命名规范(如pageNum/pageSize),便于团队协作
-
错误处理:对分页接口添加完善的错误处理和重试机制
-
用户体验:添加适当的过渡动画和加载状态,提升交互体验
-
测试覆盖:分页逻辑是bug高发区,需要重点测试边界条件
这个Demo项目已经包含了Ruoyi+Vue2下最常用的三种分页实现方案,你可以直接基于这些代码进行扩展开发。根据我的经验,合理选择分页方案可以显著提升大型数据应用的性能和用户体验。
