Vue学习笔记-分页表格最佳实践(封装 Table + 分页组件) - 详解
作者:一名 Vue 的学习者
记录时间:2025年12月
目标:熟练使用分页组件,实现如下效果:

写到这个阶段,我已经能做页面、布局、菜单、登录、Echarts、Axios 封装等基础能力。
但实际开发中最常见的场景之一——表格 + 分页,才是真正决定开发效率的地方。
一个“好用的分页表格组件”,能让后台管理系统的开发效率翻倍。
这篇我不只使用表格,而是要实现:
✔ 封装一个通用的 Table + Pagination 组件
✔ 支持自动分页、加载状态
✔ 支持外部传入查询参数
✔ 支持点击分页自动触发重新请求
✔ 让页面逻辑只保留 5 行代码
一、为什么要封装分页表格?
实际开发中,如果不封装,几乎每个列表页面都长这样:
const list = ref([]);
const page = ref(1);
const size = ref(10);
const total = ref(0);
const fetchList = async () => {
const res = await api.getList({ page: page.value, size: size.value });
list.value = res.records;
total.value = res.total;
};
并且:
- 分页切换要重新请求
- 重置搜索要重置到第一页
- 页面加载要自动请求
- loading 控制又写一遍
一个系统几十个列表页面,都是重复代码。
封装组件后,我们的列表页可以变成:
只写一行,舒服!
二、基础组件结构设计
这里打算把组件拆成两个文件:
✔ BaseTable.vue(组合表格 + 分页 + loading)
✔ BasePagination.vue(分页组件)
目录如下:
src/components/base/
BaseTable.vue
BasePagination.vue
三、先做分页组件 BasePagination.vue
分页组件功能:
- 切换页码触发事件
- 切换条数触发事件
用 Element Plus 会简单很多,下文以 Element Plus 为例。
BasePagination.vue
<script setup>
const props = defineProps({
total: Number,
currentPage: Number,
pageSize: Number
});
const emits = defineEmits(["update:currentPage", "update:pageSize", "change"]);
const onPageChange = (val) => {
emits("update:currentPage", val);
emits("change");
};
const onSizeChange = (val) => {
emits("update:pageSize", val);
emits("change");
};
</script>
支持:
- v-model:currentPage
- v-model:pageSize
- change(统一触发外部刷新)
四、封装 BaseTable(重点)
目标:
✔ 接收 columns
✔ 接收 request(统一分页请求函数)
✔ 自动处理 loading
✔ 自动请求数据
✔ 内置分页组件
✔ 列内容脱敏
✔ 自定义操作列
BaseTable.vue
{{ (page - 1) * pageSize + scope.$index + 1 }}
编辑
删除
{{
rawCol.formatter
? rawCol.formatter(scope.row[rawCol.prop], scope.row)
: scope.row[rawCol.prop]
}}
<script setup>
import { ref, watch, onMounted } from "vue";
import BasePagination from "./BasePagination.vue";
/* ========= props ========= */
const props = defineProps({
columns: {
type: Array,
default: () => []
},
request: {
type: Function,
required: true
},
params: {
type: Object,
default: () => ({})
}
});
/* ========= 列类型预设 ========= */
const COLUMN_PRESET = {
index: {
width: 70,
align: "center"
},
action: {
width: 160,
align: "center"
},
short: {
width: 100,
align: "left"
},
normal: {
minWidth: 140,
align: "left"
},
long: {
minWidth: 220,
align: "left",
tooltip: true
},
time: {
width: 180,
align: "center"
},
status: {
width: 90,
align: "center"
}
};
/* ========= 工具方法 ========= */
const resolveColumn = (col) => {
const preset = COLUMN_PRESET[col.type] || {};
return {
...preset,
...col
};
};
/* ========= 表格状态 ========= */
const tableData = ref([]);
const loading = ref(false);
const total = ref(0);
const page = ref(1);
const pageSize = ref(10);
/* ========= 数据请求 ========= */
const fetchList = async () => {
if (!props.request) return;
loading.value = true;
try {
const res = await props.request({
current: page.value,
size: pageSize.value,
...props.params
});
tableData.value = res?.records || [];
total.value = res?.total || 0;
} finally {
loading.value = false;
}
};
onMounted(fetchList);
watch(
() => props.params,
() => {
page.value = 1;
fetchList();
},
{ deep: true }
);
defineExpose({
fetchList
});
</script>
五、如何在页面中使用?
假设我们有个用户接口:
// api/user.js
export function getUserList(params) {
return request({
url: "/user/list",
method: "get",
params
});
}
页面中使用
重置
{{ row.status === 1 ? '启用' : '禁用' }}
编辑
删除
<script setup>
import BaseTable from "@/components/base/BaseTable.vue";
import * as api from "@/api/user";
import {ref} from "vue";
import {maskMobile, maskName} from '@/utils/mask';
const tableRef = ref();
const columns = [
{ type: 'index' },
{ label: "姓名", prop: "name" ,formatter: maskName, type: 'short' },
{ label: "手机号", prop: "phone" ,formatter: maskMobile , type: 'normal'},
// { label: "邮箱", prop: "email", type: 'normal' },
{ label: "创建时间", prop: "createTime" , type: 'time'},
{ prop: 'status', label: '状态', type: 'status', slot: 'status' },
{ type: 'action' }
];
const searchForm = ref({
phone: '',
username: '',
});
function handleReset() {
searchForm.value = {};
tableRef.value?.fetchList();
}
function edit(row) {
//todo
}
function remove(row) {
//todo
}
</script>
现在一个分页列表页面几乎不需要逻辑了。
六、扩展:表格插槽支持更多内容
为了支持“操作列、按钮、格式化”等,BaseTable 增加了插槽:
页面即可:
编辑
七、小结
本篇实现了一个真正可复用的分页表格组件,让我在后台系统开发过程中的效率提升非常明显。
本篇成果:
✔ BasePagination
- 封装 Element Plus 分页
- 简化分页切换逻辑
- 支持 v-model 双向绑定
✔ BaseTable
- 自动触发分页请求
- 自动处理 loading、data、total
- 支持外部搜索参数
- 支持表格插槽扩展
- 支持自定义列脱敏
- 页面逻辑极致简化
✔ 实际效果
现在一个分页列表页面,只需要写:
实现了前后端项目中最常见的一类页面,真正做到“写一次,到处用”。
下一篇预告
下一步计划:树形结构学习

浙公网安备 33010602011771号