在Next.js站点实现Load More分页按钮,解决大量文章加载崩溃问题
解决方案:Next.js + WordPress 博客实现「加载更多」分页
问题背景
基于Next.js搭建的博客采用WordPress作为API数据源,当文章数量超过500篇后,因一次性拉取1000篇文章的全量数据,导致博客频繁崩溃。需要实现「Load More」分页按钮,点击时仅获取指定数量的文章数据。
当前代码分析
1. 博客首页文件(index.tsx)
//index.tsx /** * Caution: Consider this file when using NextJS or GatsbyJS * * You may delete this file and its occurrences from the project filesystem if you are using react-scripts */ import React from 'react'; import BlogSearch from 'views/BlogSearch'; import Main from 'layouts/Main'; import WithLayout from 'WithLayout'; import Head from 'next/head'; import { getBlogPosts } from '../../lib/api'; const BlogPage = ({ posts }): JSX.Element => { return ( <> {' '} <Head> <title>Copy and Post - Blog</title> <meta property="og:title" content="Copy and Post - Blog" key="Copy and Post" /> </Head> <WithLayout component={() => <BlogSearch posts={posts} />} layout={Main} /> </> ); }; export default BlogPage; export async function getStaticProps() { const posts = await getBlogPosts(); return { props: { ...posts, }, }; }
2. API功能文件(api.ts)
//api.ts import { homepageQuery, blogPosts, blogPostBySlug } from './queries'; const API_URL = process.env.WP_API_URL; async function fetchAPI(query, { variables = {} } = {}) { // Set up some headers to tell the fetch call // that this is an application/json type const headers = { 'Content-Type': 'application/json' }; // build out the fetch() call using the API_URL // environment variable pulled in at the start // Note the merging of the query and variables const res = await fetch(API_URL, { method: 'POST', headers, body: JSON.stringify({ query, variables }), }); // error handling work const json = await res.json(); if (json.errors) { console.log(json.errors); console.log('error details', query, variables); throw new Error('Failed to fetch API'); } return json.data; } export async function getHomepageSections() { const data = await fetchAPI(homepageQuery); return data; } export async function getBlogPosts() { const data = await fetchAPI(blogPosts); return data; } export async function getBlogPostBySlug(slug) { const data = await fetchAPI(blogPostBySlug, { variables: { id: slug } }); return data; }
3. 查询函数文件(query.ts)
//query.ts export const homepageQuery = ` query HomepageQuery { homepageSections { edges { node { homepage { hero { animatedwords heading subtitle } callouts { title subtitle calloutone { title subtext image { mediaItemUrl } } calloutthree { title subtext image { mediaItemUrl } } callouttwo { title subtext image { mediaItemUrl } } } icongrid { iconone { description icon title } iconfive { description icon title } iconfour { description icon title } iconsix { description icon title } iconthree { description icon title } icontwo { description icon title } } } } } } } `; export const blogPosts = ` query BlogPosts { posts(first: 1000) { edges { node { author { node { nickname } } date slug featuredImage { node { mediaItemUrl } } title } } } } `; export const blogPostBySlug = ` query PostBySlug($id: ID!) { post(id: $id, idType: SLUG) { id content title author { node { nickname avatar { url } } } date featuredImage { node { mediaItemUrl } } } } `;
4. 控制台报错截图

分步实现「加载更多」功能
步骤1:修改GraphQL查询,支持分页参数
更新query.ts中的blogPosts查询,添加游标分页参数和分页信息字段:
export const blogPosts = ` query BlogPosts($first: Int!, $after: String) { posts(first: $first, after: $after) { edges { node { author { node { nickname } } date slug featuredImage { node { mediaItemUrl } } title } } pageInfo { hasNextPage endCursor } } } `;
新增的pageInfo用于判断是否还有下一页,以及获取当前页的结束游标,作为下一页查询的起始标记。
步骤2:更新API函数,支持传入分页参数
修改api.ts中的getBlogPosts函数,接收分页参数:
export async function getBlogPosts(first = 10, after = null) { const data = await fetchAPI(blogPosts, { variables: { first, after } }); return data; }
默认每次加载10篇文章,可根据需求调整数量。
步骤3:改造博客首页组件,实现客户端分页逻辑
更新index.tsx,添加状态管理和加载更多逻辑:
//index.tsx import React, { useState } from 'react'; import BlogSearch from 'views/BlogSearch'; import Main from 'layouts/Main'; import WithLayout from 'WithLayout'; import Head from 'next/head'; import { getBlogPosts } from '../../lib/api'; const BlogPage = ({ initialPosts, initialPageInfo }): JSX.Element => { const [posts, setPosts] = useState(initialPosts.edges); const [pageInfo, setPageInfo] = useState(initialPageInfo); const [isLoading, setIsLoading] = useState(false); const loadMorePosts = async () => { if (!pageInfo.hasNextPage || isLoading) return; setIsLoading(true); try { const newData = await getBlogPosts(10, pageInfo.endCursor); setPosts(prev => [...prev, ...newData.posts.edges]); setPageInfo(newData.posts.pageInfo); } catch (error) { console.error('加载更多失败:', error); } finally { setIsLoading(false); } }; return ( <> <Head> <title>Copy and Post - Blog</title> <meta property="og:title" content="Copy and Post - Blog" key="Copy and Post" /> </Head> <WithLayout component={() => ( <> <BlogSearch posts={{ edges: posts }} /> {pageInfo.hasNextPage && ( <button onClick={loadMorePosts} disabled={isLoading} style={{ padding: '10px 20px', marginTop: '20px', cursor: 'pointer' }} > {isLoading ? '加载中...' : '加载更多'} </button> )} </> )} layout={Main} /> </> ); }; export default BlogPage; export async function getStaticProps() { const data = await getBlogPosts(10); return { props: { initialPosts: data.posts, initialPageInfo: data.posts.pageInfo }, }; }
核心修改点:
- 用
useState管理已加载文章、分页信息和加载状态 - 实现
loadMorePosts函数,点击时请求下一页数据并合并到现有列表 - 初始仅加载10篇文章,避免一次性拉取大量数据
- 根据分页信息动态显示/隐藏加载按钮,加载中时禁用按钮
步骤4:兼容BlogSearch组件
确保BlogSearch组件能正确遍历posts.edges渲染文章,现有逻辑无需额外修改即可兼容。
效果验证
部署后首页仅加载10篇文章,点击「加载更多」才会请求下一批数据,彻底解决全量加载导致的崩溃问题。当没有更多文章时,按钮会自动隐藏。
内容的提问来源于stack exchange,提问作者Udemezue John
相关产品推荐
相关产品推荐

