You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Astro与Strapi CMS实现SSR分页的问题求助

Astro SSR模式下结合Strapi实现分页的正确方案

问题背景

在Astro项目中结合Strapi CMS实现分页,可通过传递每页条目数控制分页响应:
strapi分页响应元数据

注意:使用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友好。

  1. 修改首页组件,从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>
  1. 修改分页组件,通过链接跳转更新页码
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状态管理页码,实现无刷新切换分页,用户体验更流畅。

  1. 修改首页组件,封装客户端分页逻辑
---
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>
  1. 创建客户端组件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>
    )
}
  1. 更新分页组件,接收回调函数
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:57:01