基于Astro与Strapi CMS实现SSR分页的问题求助
Astro SSR模式下结合Strapi实现分页的正确方案
问题背景
在Astro项目中结合Strapi CMS实现分页,可通过传递每页条目数控制分页响应:
注意:使用SSR模式,无法使用getStaticPaths及Astro内置分页功能
当前核心问题:点击分页组件的"Next"按钮后,控制台能看到页码递增,但父组件的currentPage值未更新,导致无法获取第二页数据。
现有代码片段
数据获取函数
export const fetchArticles = async (page = 1, pageSize = 5, sort = 'createdAt:desc') => { const articles = await fetchApi<Article>({ endpoint: 'articles', query: { populate: ['category', 'cover'].join(','), 'pagination[page]': String(page), 'pagination[pageSize]': String(pageSize), sort: sort } }) return articles }
分页组件(React TSX)
import { useState } from 'react' interface Props { metaData: Pagination currentPage: number } export default function NewPagination(props: Props) { let { metaData, currentPage } = props const increaseCurrentPage = () => { currentPage++ console.log(currentPage) } return ( <div> <nav className='text-center'> <ul className='inline-flex -space-x-px text-base h-10'> <li> <a className='flex items-center justify-center px-4 h-10 ms-0 leading-tight text-gray-500 bg-white border border-e-0 border-gray-300 rounded-s-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white'> Previous </a> </li> <li>{currentPage}</li> <li> <a onClick={increaseCurrentPage} className='flex items-center justify-center px-4 h-10 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white' > Next </a> </li> </ul> </nav> <script></script> </div> ) }
首页组件
--- import ListCategories from '@/components/ListCategories' import ListPosts from '@/components/ListPosts' import NewPagination from '@/components/NewPagination' import TitlePage from '@/components/TitlePage' import BaseLayout from '@/layouts/BaseLayout' import Swiper from 'src/components/swiper/Swiper' import { fetchArticles } from 'src/utils/fetchApiData' let currentPage = 1 const articles = await fetchArticles(currentPage, 5) const allArticles = articles.data.map((article) => article) const swiperContent = allArticles.slice(0, 4) // const metaData = articles.meta.pagination --- <BaseLayout title=''> <Swiper client:load swiperContent={swiperContent} /> <TitlePage title={'All Articles'} /> <ListCategories /> <ListPosts FirstBig={true} allArticles={allArticles} /> <NewPagination /> </BaseLayout> <script> import { animate } from 'motion' const showAnimations = localStorage.getItem('animations') === 'true' if (showAnimations) { animate( '.title', { y: [80, 0], opacity: [0, 1] }, { duration: 2.5, opacity: { duration: 3 }, offset: [0, 0.55, 0.75] } ) } </script>
解决方案
方案一:基于URL查询参数的SSR分页(页面刷新式)
利用Astro SSR特性,通过URL的page查询参数控制页码,每次切换触发服务器端重新获取数据,SEO友好。
- 修改首页组件,从URL获取页码
--- import ListCategories from '@/components/ListCategories' import ListPosts from '@/components/ListPosts' import NewPagination from '@/components/NewPagination' import TitlePage from '@/components/TitlePage' import BaseLayout from '@/layouts/BaseLayout' import Swiper from 'src/components/swiper/Swiper' import { fetchArticles } from 'src/utils/fetchApiData' import { getURL } from 'astro:request' // 从URL查询参数读取页码,默认值为1 const url = new URL(getURL()) const currentPage = parseInt(url.searchParams.get('page') || '1') const articles = await fetchArticles(currentPage, 5) const allArticles = articles.data.map((article) => article) const swiperContent = allArticles.slice(0, 4) const metaData = articles.meta.pagination --- <BaseLayout title=''> <Swiper client:load swiperContent={swiperContent} /> <TitlePage title={'All Articles'} /> <ListCategories /> <ListPosts FirstBig={true} allArticles={allArticles} /> {/* 传递分页元数据和当前页码 */} <NewPagination metaData={metaData} currentPage={currentPage} /> </BaseLayout> <script> import { animate } from 'motion' const showAnimations = localStorage.getItem('animations') === 'true' if (showAnimations) { animate( '.title', { y: [80, 0], opacity: [0, 1] }, { duration: 2.5, opacity: { duration: 3 }, offset: [0, 0.55, 0.75] } ) } </script>
- 修改分页组件,通过链接跳转更新页码
interface Props { metaData: Pagination currentPage: number } export default function NewPagination(props: Props) { const { metaData, currentPage } = props const totalPages = metaData.pageCount // 生成目标页码的URL const getPageUrl = (page: number) => { const url = new URL(window.location.href) url.searchParams.set('page', page.toString()) return url.href } return ( <div> <nav className='text-center'> <ul className='inline-flex -space-x-px text-base h-10'> <li> {/* 上一页按钮,页码为1时禁用 */} <a href={getPageUrl(currentPage - 1)} className={`flex items-center justify-center px-4 h-10 ms-0 leading-tight ${currentPage === 1 ? 'text-gray-300 bg-gray-100 cursor-not-allowed' : 'text-gray-500 bg-white hover:bg-gray-100 hover:text-gray-700'} border border-e-0 border-gray-300 rounded-s-lg dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white`} onClick={(e) => currentPage === 1 && e.preventDefault()} > Previous </a> </li> <li className='flex items-center justify-center px-4 h-10 leading-tight text-blue-600 border border-gray-300 bg-blue-50 dark:bg-gray-700 dark:border-gray-700 dark:text-white'> {currentPage} </li> <li> {/* 下一页按钮,页码等于总页数时禁用 */} <a href={getPageUrl(currentPage + 1)} className={`flex items-center justify-center px-4 h-10 leading-tight ${currentPage === totalPages ? 'text-gray-300 bg-gray-100 cursor-not-allowed' : 'text-gray-500 bg-white hover:bg-gray-100 hover:text-gray-700'} border border-gray-300 rounded-e-lg dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white`} onClick={(e) => currentPage === totalPages && e.preventDefault()} > Next </a> </li> </ul> </nav> </div> ) }
方案二:客户端动态加载(无刷新分页)
将数据获取逻辑移到客户端,使用React状态管理页码,实现无刷新切换分页,用户体验更流畅。
- 修改首页组件,封装客户端分页逻辑
--- import ListCategories from '@/components/ListCategories' import TitlePage from '@/components/TitlePage' import BaseLayout from '@/layouts/BaseLayout' import Swiper from 'src/components/swiper/Swiper' import { fetchArticles } from 'src/utils/fetchApiData' // 预加载第一页数据 const initialArticles = await fetchArticles(1, 5) const swiperContent = initialArticles.data.slice(0, 4) --- <BaseLayout title=''> <Swiper client:load swiperContent={swiperContent} /> <TitlePage title={'All Articles'} /> <ListCategories /> {/* 客户端组件处理分页逻辑 */} <ClientPostsList initialArticles={initialArticles} pageSize={5} /> </BaseLayout> <script> import { animate } from 'motion' const showAnimations = localStorage.getItem('animations') === 'true' if (showAnimations) { animate( '.title', { y: [80, 0], opacity: [0, 1] }, { duration: 2.5, opacity: { duration: 3 }, offset: [0, 0.55, 0.75] } ) } </script>
- 创建客户端组件
ClientPostsList.tsx
import { useState } from 'react' import ListPosts from './ListPosts' import NewPagination from './NewPagination' import { fetchArticles } from '../utils/fetchApiData' import type { ArticleResponse, Pagination } from '../types' interface Props { initialArticles: ArticleResponse pageSize: number } export default function ClientPostsList(props: Props) { const [articles, setArticles] = useState(props.initialArticles) const [currentPage, setCurrentPage] = useState(1) const [isLoading, setIsLoading] = useState(false) // 加载指定页码的数据 const fetchPage = async (page: number) => { setIsLoading(true) try { const newArticles = await fetchArticles(page, props.pageSize) setArticles(newArticles) setCurrentPage(page) } catch (err) { console.error('加载文章失败:', err) } finally { setIsLoading(false) } } const handleNext = () => { if (currentPage < articles.meta.pagination.pageCount) { fetchPage(currentPage + 1) } } const handlePrev = () => { if (currentPage > 1) { fetchPage(currentPage - 1) } } return ( <div> {isLoading && <div className='text-center py-4'>加载中...</div>} <ListPosts FirstBig={true} allArticles={articles.data} /> <NewPagination metaData={articles.meta.pagination} currentPage={currentPage} onNext={handleNext} onPrev={handlePrev} /> </div> ) }
- 更新分页组件,接收回调函数
interface Props { metaData: Pagination currentPage: number onNext: () => void onPrev: () => void } export default function NewPagination(props: Props) { const { metaData, currentPage, onNext, onPrev } = props const totalPages = metaData.pageCount return ( <div> <nav className='text-center'> <ul className='inline-flex -space-x-px text-base h-10'> <li> <button onClick={onPrev} disabled={currentPage === 1} className={`flex items-center justify-center px-4 h-10 ms-0 leading-tight ${currentPage === 1 ? 'text-gray-300 bg-gray-100 cursor-not-allowed' : 'text-gray-500 bg-white hover:bg-gray-100 hover:text-gray-700'} border border-e-0 border-gray-300 rounded-s-lg dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white`} > Previous </button> </li> <li className='flex items-center justify-center px-4 h-10 leading-tight text-blue-600 border border-gray-300 bg-blue-50 dark:bg-gray-700 dark:border-gray-700 dark:text-white'> {currentPage} </li> <li> <button onClick={onNext} disabled={currentPage === totalPages} className={`flex items-center justify-center px-4 h-10 leading-tight ${currentPage === totalPages ? 'text-gray-300 bg-gray-100 cursor-not-allowed' : 'text-gray-500 bg-white hover:bg-gray-100 hover:text-gray-700'} border border-gray-300 rounded-e-lg dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white`} > Next </button> </li> </ul> </nav> </div> ) }
内容的提问来源于stack exchange,提问作者TechnoXDroid
相关产品推荐
相关产品推荐

