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

Next.js静态站点中getStaticProps分页参数添加失败的解决问询

静态站点下Next.js + GraphQL分页实现方案

由于你需要构建静态站点,无法使用getServerSideProps,推荐采用客户端动态查询的方式实现分页,核心思路是:构建时通过getStaticProps生成第一页的静态内容,后续分页数据在客户端通过数据请求工具动态获取,既满足静态站点的构建要求,又能实现动态分页功能。下面给出两种实用方案:

方案一:使用SWR实现客户端数据获取与缓存

SWR是Next.js官方推荐的数据请求库,自带缓存、重验证等特性,非常适合静态站点的动态数据需求。

步骤1:安装依赖

npm install swr

步骤2:封装GraphQL请求函数

async function fetchCharacters(page) {
  const res = await fetch('https://rickandmortyapi.com/graphql', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        query GetCharacters($page: Int) {
          characters(page: $page) {
            results {
              id
              name
              status
              species
            }
            info {
              pages
              next
              prev
            }
          }
        }
      `,
      variables: { page }
    })
  });
  const data = await res.json();
  return data.data;
}

步骤3:改造页面组件

import { useState } from 'react';
import useSWR from 'swr';

// 构建时预获取第一页数据,作为静态内容的一部分
export async function getStaticProps() {
  const initialData = await fetchCharacters(1);
  return { props: { initialData } };
}

export default function CharactersPage({ initialData }) {
  const [currentPage, setCurrentPage] = useState(1);
  // 用SWR根据当前页码动态请求数据,复用初始数据避免重复请求
  const { data, error, isLoading } = useSWR(
    [currentPage],
    () => fetchCharacters(currentPage),
    { initialData }
  );

  if (error) return <div>加载失败,请稍后重试</div>;
  if (isLoading && !data) return <div>加载中...</div>;

  const { characters } = data;

  return (
    <div>
      <table border="1" cellPadding="8">
        <thead>
          <tr>
            <th>ID</th>
            <th>角色名</th>
            <th>状态</th>
            <th>物种</th>
          </tr>
        </thead>
        <tbody>
          {characters.results.map(char => (
            <tr key={char.id}>
              <td>{char.id}</td>
              <td>{char.name}</td>
              <td>{char.status}</td>
              <td>{char.species}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <div style={{ marginTop: '20px', display: 'flex', gap: '10px', alignItems: 'center' }}>
        <button
          onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
          disabled={currentPage === 1}
        >
          上一页
        </button>
        <span>第 {currentPage} 页 / 共 {characters.info.pages} 页</span>
        <button
          onClick={() => setCurrentPage(prev => Math.min(prev + 1, characters.info.pages))}
          disabled={currentPage === characters.info.pages}
        >
          下一页
        </button>
      </div>
    </div>
  );
}

方案二:使用Apollo Client实现客户端查询

如果你的项目已经在使用Apollo Client,可以用它来实现动态分页:

步骤1:配置Apollo Client

在项目中创建apollo-client.js:

import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';

const httpLink = createHttpLink({
  uri: 'https://rickandmortyapi.com/graphql',
});

export const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
});

步骤2:在页面中使用Apollo Client

import { useState } from 'react';
import { useQuery, gql } from '@apollo/client';
import { client } from '../apollo-client';

const GET_CHARACTERS = gql`
  query GetCharacters($page: Int) {
    characters(page: $page) {
      results {
        id
        name
        status
        species
      }
      info {
        pages
        next
        prev
      }
    }
  }
`;

export async function getStaticProps() {
  const { data } = await client.query({
    query: GET_CHARACTERS,
    variables: { page: 1 }
  });
  return { props: { initialData: data } };
}

export default function CharactersPage({ initialData }) {
  const [currentPage, setCurrentPage] = useState(1);
  const { loading, error, data } = useQuery(GET_CHARACTERS, {
    variables: { page: currentPage },
    initialData
  });

  if (error) return <div>加载失败</div>;
  if (loading && !data) return <div>加载中...</div>;

  const { characters } = data;

  // 分页按钮和表格渲染逻辑同方案一
  return (
    <div>
      {/* 表格代码 */}
      {/* 分页按钮代码 */}
    </div>
  );
}

关键注意事项

  1. 避免Hydration不匹配:通过getStaticProps预取的第一页数据,要作为客户端请求工具的初始数据传入,保证服务端渲染内容和客户端初始化内容一致。
  2. 状态管理:用React状态currentPage控制当前页码,点击分页按钮时更新状态,触发数据重新请求。
  3. 错误与加载状态:必须处理数据请求中的加载、错误场景,提升用户体验。
  4. SEO兼容性:静态生成的第一页内容完全支持SEO,客户端渲染的后续页内容,现代搜索引擎也能正常抓取解析,如果有极高SEO需求,可以考虑预生成所有页面(但灵活性较差)。

内容的提问来源于stack exchange,提问作者ChristianOConnor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:10:43