Material UI Pagination 分页组件详解:siblingCount、受控模式、usePagination Hook 与路由集成实战
本文以 Material UI(MUI)官方文档 Pagination 组件页 为主体,覆盖从基础用法、页码范围裁剪(siblingCount / boundaryCount)、受控分页、路由集成到无头 Hook usePagination 的完整技术脉络,并结合 Pagination 组件源码 给出每个属性的默认值与底层实现依据,帮助你在博客、电商列表、后台表格等真实场景中正确选择 Pagination 或 TablePagination,并写出可复制运行的分页代码。
组件定位与源码结构
Pagination 组件让用户从一个页面范围中选择特定页码。它适用于「不使用无限加载」时对任意条目列表进行分页的场景,官方文档明确指出:在 SEO 很重要的上下文(例如博客) 中优先使用 Pagination;而对大量表格数据分页,则应使用 TablePagination 组件。
组件的源码位于 packages/mui-material/src/Pagination 目录,核心文件包括:
- Pagination.js:组件主体,负责将
usePagination返回的条目映射为PaginationItem并管理焦点逻辑; - paginationClasses.ts:定义
MuiPagination的类名工具(root、ul、outlined、text); - 配套的无头 Hook
usePagination(位于 packages/mui-material/src/usePagination)与条目组件PaginationItem(位于packages/mui-material/src/PaginationItem),三者共同构成分页体系的完整分层。
基础用法
最简示例只需 count(总页数)一个必填语义的属性,其余全部有默认值。基础示例见 BasicPagination:
import Pagination from '@mui/material/Pagination';
export default function BasicPagination() {
return <Pagination count={10} />;
}
从 Pagination.js 源码 中解构出的默认值可以确认各属性缺省行为:
| 属性 | 默认值 | 说明 |
|---|---|---|
count |
1 |
总页数 |
page |
— | 当前页(受控),从 1 开始 |
defaultPage |
1 |
非受控模式下的初始页码 |
variant |
'text' |
外观变体,可选 outlined |
shape |
'circular' |
页码按钮形状,可选 rounded |
size |
'medium' |
尺寸,可选 small、large |
color |
'standard' |
选中页颜色,支持主题调色板颜色 |
siblingCount |
1 |
当前页前后各显示几页 |
boundaryCount |
1 |
首尾固定显示几页 |
showFirstButton / showLastButton |
false |
是否显示「首页 / 末页」按钮 |
hidePrevButton / hideNextButton |
false |
是否隐藏「上一页 / 下一页」按钮 |
renderItem |
(item) => <PaginationItem {...item} /> |
自定义每个分页条目的渲染 |
外观变体:变体、形状与尺寸
文档提供了三组外观示例,分别对应 variant、shape、size 三个属性:
- Outlined pagination:
variant="outlined",示例见 PaginationOutlined; - Rounded pagination:
shape="rounded",示例见 PaginationRounded; - Pagination size:
size="small"或"large",示例见 PaginationSize。
在样式层面,Pagination.js 中根节点被 styled('nav') 定义,overridesResolver 会按 ownerState.variant 叠加 outlined / text 对应的类名;内部 <ul> 固定为 flex 布局、flexWrap: 'wrap'、无列表样式,因此分页天然支持窄容器下的换行展示。
首页 / 末页按钮与隐藏前后翻页按钮
文档的 Buttons 章节说明:可以可选地启用首页、末页按钮,或禁用上一页、下一页按钮。示例见 PaginationButtons,对应属性为 showFirstButton、showLastButton、hidePrevButton、hideNextButton,四个布尔属性均可自由组合。
一个容易踩的边界情况被源码显式处理了:当你在第 1 页点击「首页/上一页」、或在末页点击「下一页/末页」时,该按钮点击后会变为 disabled。如果此时焦点正停留在该按钮上,焦点会「丢失」,handleItemClick 与焦点恢复逻辑 会把焦点记录到 pendingFocusRef,并在 selectedPage 更新后自动把焦点移到带 aria-current="page" 的选中页上。使用 usePagination 自行渲染时需要注意复刻这一行为。
自定义控制图标
文档说明控制图标(前后翻页箭头)可以自定义,示例见 CustomIcons,通过 showFirstButton、showLastButton 与自定义 renderItem 将 PaginationItem 的箭头替换为自定义 SVG/图标即可。
页码范围:siblingCount 与 boundaryCount
这是 Pagination 最核心的两个数字属性:
siblingCount:控制页码省略号两侧显示的数字个数(相对当前页);boundaryCount:控制首尾页号旁固定显示的页码个数。
官方示例 PaginationRanges 展示了四组组合(count={11}、defaultPage={6}):
<Stack spacing={2}>
<Pagination count={11} defaultPage={6} siblingCount={0} />
<Pagination count={11} defaultPage={6} /> {/* Default ranges */}
<Pagination count={11} defaultPage={6} siblingCount={0} boundaryCount={2} />
<Pagination count={11} defaultPage={6} boundaryCount={2} />
</Stack>
结合默认值 siblingCount=1、boundaryCount=1,默认渲染形如 1 … 5 6 7 … 11;将 siblingCount 设为 0 则只剩当前页与边界页,将 boundaryCount 设为 2 则首尾各固定显示两页。这两个参数共同决定了条目数组中 page、start-ellipsis、end-ellipsis 三类条目的分布,其裁剪逻辑全部收敛在 usePagination 内部,组件层只负责渲染。
受控分页
非受控模式下用 defaultPage 指定初始页,onChange 回调签名是 onChange(event, page),其中 page 从 1 开始。受控模式则由外部状态接管,核心写法(对应 PaginationControlled 示例):
export default function PaginationControlled() {
const [page, setPage] = React.useState(1);
const handleChange = (event, value) => {
setPage(value);
};
return <Pagination page={page} count={10} onChange={handleChange} />;
}
传入 page 后组件进入受控模式,页码变化只会触发 onChange,需要你在回调中更新状态;Pagination 的 page prop 从 1 开始编号(源码 PropTypes 注释亦强调这一点,见 Pagination.js)。
路由集成:renderItem + 自定义组件
对于需要把页码写进 URL 的场景(SEO 友好),文档的 Router integration 章节给出了标准做法:通过 renderItem 把 PaginationItem 的 component 换成路由的 Link。完整可运行的 PaginationLink 示例:
function Content() {
const location = useLocation();
const query = new URLSearchParams(location.search);
const page = parseInt(query.get('page') || '1', 10);
return (
<Pagination
page={page}
count={10}
renderItem={(item) => (
<PaginationItem
component={Link}
to={`/inbox${item.page === 1 ? '' : `?page=${item.page}`}`}
{...item}
/>
)}
/>
);
}
要点:renderItem 接收的每个 item 都携带 page、type、selected、onClick 等字段,展开为 PaginationItem 的 props 即可;第 1 页的 URL 刻意不带查询参数,保证首页链接干净可分享。
无头 Hook:usePagination
文档明确:usePagination() 是一个无头 Hook,面向高级定制场景暴露,它接受与 Pagination 组件几乎相同的选项,只是去掉了所有与 JSX 渲染相关的 prop;Pagination 组件正是构建在这个 Hook 之上的。
import usePagination from '@mui/material/usePagination';
Hook 的返回值为 { items, ... },items 中每一项的 type 取值包含 first、previous、page、next、last、start-ellipsis、end-ellipsis。UsePagination 官方示例 演示了完全自行渲染:遍历 items,对 start-ellipsis / end-ellipsis 渲染省略号「…」,对 page 类型渲染原生 <button>(选中时加粗),其余类型渲染翻页按钮,并手动实现「点击后按钮禁用时移动焦点到首/末页」的无障碍逻辑——这正是 Pagination.js 中 handleItemClick 所做的事情,说明 Hook 使用者需要自行保证焦点管理的等价行为。
TablePagination:与表格配套的另一种分页
文档特别提示:为大型表格数据分页应使用 TablePagination 组件,可参考文档 table 章节的 custom pagination options 内容。两者最关键的差异在页码起点:
Pagination的page从 1 开始,以匹配「页码要出现在 URL 中」的需求;TablePagination的page从 0 开始,以匹配渲染大量表格数据时零基 JavaScript 数组切片的需求(items.slice(page * rowsPerPage, ...))。
因此在混合使用两个组件时,务必做 page 的 ±1 换算,这是实际项目中最常见的 off-by-one 错误来源。
无障碍(Accessibility)
文档的 Accessibility 章节包含两部分,均已在源码中得到印证:
ARIA:根节点默认带有 role="navigation"(源码中即渲染为 <nav>)和 aria-label="pagination navigation"(见 Pagination.js 第 158 行);每个分页条目都会获得说明其用途的 aria-label,例如 "go to first page"、"go to previous page"、"go to page 1"。这些文案默认由 defaultGetAriaLabel 生成:type === 'page' 时返回 Go to page N(选中页则无前缀 Go to),其他类型返回 Go to ${type} page。可本地化的文案可通过 getItemAriaLabel prop 覆盖,其签名为 (type, page, selected) => string。
键盘:分页条目处于 Tab 顺序中,tabindex 为 "0",配合上文提到的焦点恢复逻辑,键盘用户在翻页后焦点会稳定落在新的选中页上。
样式类名与定制
paginationClasses.ts 导出了 MuiPagination 的全部类名:root(根元素)、ul(列表容器)、outlined(variant="outlined" 时)、text(variant="text" 时),可配合 sx、classes prop 或主题 components.MuiPagination.styleOverrides 做样式覆盖。
小结
Material UI 的 Pagination 以「组件 + 无头 Hook」双层设计覆盖从开箱即用到完全自渲染的分页需求:variant / shape / size / color 控制外观,siblingCount / boundaryCount 控制页码范围,showFirstButton / showLastButton / hidePrevButton / hideNextButton 控制导航按钮,renderItem 打通路由集成,usePagination 则把条目计算逻辑开放给定制场景;而表格数据分页请切换到 TablePagination 并注意两者页码起点(1 基 vs 0 基)的差异。所有属性默认值均可在 packages/mui-material/src/Pagination/Pagination.js 中逐一对应验证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00