在NextJS 13中使用getStaticProps实现分页的方式是否合理?
Next.js 13 分页实现规范分析
你的当前实现逻辑上可以运行,但从性能、扩展性和Next.js最佳实践角度来看,存在不少可优化的点,具体分析和改进方向如下:
现有实现的问题
全量数据拉取的性能隐患
你通过getStaticProps在构建阶段一次性拉取所有车手数据,虽然前端分页切换流畅,但如果未来F1车手数据量增长,会导致:- 项目构建时间大幅延长
- 页面初始加载的JS包体积膨胀,拖慢首屏加载速度
- 无法获取构建完成后的最新数据(除非重新触发构建)
分页无边界校验
当前逻辑没有限制page的取值范围,当用户手动修改URL或误触按钮导致page小于1或大于总页数时,displayedDrivers会返回空数组,影响用户体验。
优化方案
方案1:保留静态生成,按页拆分静态页面
如果希望继续使用静态生成,可以结合getStaticPaths和getStaticProps,为每一页生成独立的静态页面:
// app/drivers/[page]/page.js import { useRouter } from 'next/navigation' export async function generateStaticParams() { // 先拉取总数据量,计算总页数 const res = await fetch('http://ergast.com/api/f1/drivers.json?limit=1') const json = await res.json() const total = parseInt(json.MRData.total) const totalPages = Math.ceil(total / 50) // 生成所有页面参数 return Array.from({ length: totalPages }, (_, i) => ({ page: (i + 1).toString() })) } export default async function DriversPage({ params }) { const router = useRouter() const page = parseInt(params.page) const offset = (page - 1) * 50 const res = await fetch(`http://ergast.com/api/f1/drivers.json?limit=50&offset=${offset}`) const json = await res.json() const drivers = json.MRData.DriverTable.Drivers const total = parseInt(json.MRData.total) const totalPages = Math.ceil(total / 50) const handleNextPage = () => { if (page < totalPages) { router.push(`/drivers/${page + 1}`) } } const handlePreviousPage = () => { if (page > 1) { router.push(`/drivers/${page - 1}`) } } return ( <div> {/* 渲染车手列表 */} <button onClick={handlePreviousPage} disabled={page === 1}>上一页</button> <button onClick={handleNextPage} disabled={page === totalPages}>下一页</button> </div> ) }
方案2:客户端按需分页(更推荐)
如果数据可能频繁更新,或者不想承担全量静态生成的成本,可以把数据获取逻辑移到客户端,每次切换页面时才拉取对应页的数据:
'use client' import { useState, useEffect } from 'react' export default function Drivers() { const [page, setPage] = useState(1) const [drivers, setDrivers] = useState([]) const [totalPages, setTotalPages] = useState(0) const [loading, setLoading] = useState(true) useEffect(() => { const fetchDrivers = async () => { setLoading(true) const offset = (page - 1) * 50 const res = await fetch(`http://ergast.com/api/f1/drivers.json?limit=50&offset=${offset}`) const json = await res.json() setDrivers(json.MRData.DriverTable.Drivers) const total = parseInt(json.MRData.total) setTotalPages(Math.ceil(total / 50)) setLoading(false) } fetchDrivers() }, [page]) const handleNextPage = () => { if (page < totalPages) { setPage(page + 1) } } const handlePreviousPage = () => { if (page > 1) { setPage(page - 1) } } if (loading) return <div>加载中...</div> return ( <div> {/* 渲染车手列表 */} <button onClick={handlePreviousPage} disabled={page === 1}>上一页</button> <button onClick={handleNextPage} disabled={page === totalPages}>下一页</button> </div> ) }
通用优化:边界处理
无论采用哪种方案,都必须添加分页边界校验:
- 当
page为1时,禁用「上一页」按钮 - 当
page等于总页数时,禁用「下一页」按钮 - 避免
page超出有效范围导致空数据或无效请求
内容的提问来源于stack exchange,提问作者SamePop20
相关产品推荐
相关产品推荐

