Next.js中如何在getServerSideProps实现筛选分页传参
实现方案
getServerSideProps 是服务端执行的函数,无法直接读取客户端组件内的状态,所有动态筛选、分页、排序参数都要从当前页面的URL查询参数中获取。当交互触发参数更新时,通过Next.js路由方法修改URL,框架会自动重新执行getServerSideProps拉取新数据,效果和客户端useEffect监听依赖触发请求完全一致,且天然支持SEO。
第一步:改造getServerSideProps接收动态参数
getServerSideProps 默认会接收上下文对象context,其中的query字段就是当前URL携带的所有查询参数,直接把参数拼到接口请求中即可,不要硬编码固定的分页、排序值:
export const getServerSideProps = async (context) => { // 从URL参数中解构分页、排序、筛选项,配置默认值兜底 const { page = 1, limit = 20, sortBy = 'price', sortAs = 'asc' // 后续新增筛选项(如分类、价格区间)直接在此处解构即可 } = context.query; const res = await fetch(`http://localhost:4000/api/products?page=${page}&limit=${limit}&sortBy=${sortBy}&sortAs=${sortAs}`); const data = await res.json(); // 把当前查询参数一并传给组件,用于控件状态回显 return { props: { productsData: data, currentQuery: { page: Number(page), limit: Number(limit), sortBy, sortAs } } } }
第二步:添加交互逻辑,更新URL触发数据刷新
不需要在客户端额外用useEffect拉数据,直接用Next.js内置的路由方法,用户操作分页、筛选、排序控件时,把新参数拼接到URL上跳转即可,框架会自动重新执行服务端数据拉取逻辑:
import { useRouter } from 'next/router'; // 原有依赖(Row、Col、Card、Link、Rating等)保持不变 function Home({ productsData, currentQuery }) { const router = useRouter(); // 简化原有冗余的商品数据处理逻辑,空数组兜底 const products = productsData?.data?.products || []; // 通用查询参数更新方法 const updateQuery = (newParams) => { router.push({ pathname: router.pathname, query: { ...router.query, ...newParams } }); }; return ( <> <h1>Latest Products</h1> {/* 排序控件示例 */} <div className="sort-bar mb-3"> <select value={currentQuery.sortBy} onChange={(e) => updateQuery({ sortBy: e.target.value, page: 1 })} > <option value="price">按价格排序</option> <option value="createdAt">按上架时间排序</option> <option value="rating">按评分排序</option> </select> <select value={currentQuery.sortAs} onChange={(e) => updateQuery({ sortAs: e.target.value, page: 1 })} className="ml-2" > <option value="asc">升序</option> <option value="desc">降序</option> </select> </div> <Row> {products.map((product) => ( // 原有商品卡片逻辑完全不需要修改 <Col key={product.id} sm={12} md={6} lg={4} xl={3}> <Card className="my-3 p-3 rounded"> <Link href={`/product/${product.id}`}> <Card.Img src={product.image} variant="top" /> </Link> <Card.Body> <Link href={`/product/${product.id}`}> <Card.Title as="div"> <strong>{product.name}</strong> </Card.Title> </Link> <Card.Text as="div"> <Rating value={product.rating} text={`${product.numReviews} reviews`} /> </Card.Text> <Card.Text as="h3">${product.price}</Card.Text> </Card.Body> </Card> </Col> ))} </Row> {/* 分页控件示例 */} <div className="pagination-wrap mt-4"> <button disabled={currentQuery.page <= 1} onClick={() => updateQuery({ page: currentQuery.page - 1 })} > 上一页 </button> <span className="mx-3">第 {currentQuery.page} 页</span> <button // 总页数从接口返回的字段中取,此处为示例逻辑 disabled={currentQuery.page >= productsData?.data?.totalPage} onClick={() => updateQuery({ page: currentQuery.page + 1 })} > 下一页 </button> </div> {/* 可选:加载状态提示 */} {router.isLoading && <div className="loading-tip">数据加载中...</div>} </> ); } export default Home
注意事项
- 从URL query中取出的参数默认是字符串类型,涉及数字判断(如页码、每页条数)记得提前转成数值类型,避免逻辑判断出错
- 后续新增任意筛选项都遵循同样逻辑:控件收集用户选择的参数→更新URL query→getServerSideProps取参数传给后端接口即可
- 生产环境不要硬编码
localhost:4000的接口地址,建议用环境变量统一配置接口域名,避免部署后请求失败 - 如果需要更平滑的切换体验,可以在
router.isLoading为true时展示骨架屏,替代生硬的加载提示
内容的提问来源于stack exchange,提问作者Sanjeet Kunwar
相关产品推荐
相关产品推荐

