如何为NextJS静态Markdown博客列表添加分页?
Next.js 静态Markdown博客分页实现方案
针对你的需求,以下是修复并优化后的分页实现方案,解决原代码中的逻辑问题并适配Next.js App Router的SSG规则:
一、重构元数据获取函数
先拆分原函数,将「获取所有文章元数据」和「分页筛选」逻辑分离,避免重复读取文件,提升代码复用性:
// @/components/ReusableComponents/createPaginationMetaData.ts import fs from "fs"; import matter from "gray-matter"; export interface PostMetadata { slug: string; data: any; } // 获取所有Markdown文章的元数据 export const getAllPostMetadata = (path: string): PostMetadata[] => { const files = fs.readdirSync(path); const markdownPosts = files.filter((file) => file.endsWith(".md")); return markdownPosts.map((fileName) => { const fileContents = fs.readFileSync(`${path}/${fileName}`, "utf8"); const matterResult = matter(fileContents); return { slug: fileName.replace(".md", ""), data: matterResult.data, }; }); }; // 根据分页参数筛选对应页的文章 export const getPaginatedPosts = ( posts: PostMetadata[], currentPage: number, postsPerPage: number ): PostMetadata[] => { const startIndex = (currentPage - 1) * postsPerPage; return posts.slice(startIndex, startIndex + postsPerPage); };
二、修改博客列表分页页面
更新app/blog/[page].tsx,修复总页数计算错误,替换原生跳转为Next.js客户端导航组件:
// app/blog/[page].tsx import React from "react"; import Link from "next/link"; import { getAllPostMetadata, getPaginatedPosts, PostMetadata } from "@/components/ReusableComponents/createPaginationMetaData"; const POSTS_PER_PAGE = 10; // 每页展示文章数 // 生成所有分页页面的静态参数 export async function generateStaticParams() { const allPosts = getAllPostMetadata("blog"); const totalPages = Math.ceil(allPosts.length / POSTS_PER_PAGE); return Array.from({ length: totalPages }, (_, index) => ({ page: `${index + 1}`, })); } // 为每个分页页面生成SEO标题 export async function generateMetadata({ params }: { params: { page: string } }) { const currentPage = parseInt(params.page, 10) || 1; return { title: `博客列表 - 第 ${currentPage} 页`, }; } export default function BlogList({ params }: { params: { page: string } }) { const currentPage = parseInt(params.page, 10) || 1; const allPosts = getAllPostMetadata("blog"); const totalPages = Math.ceil(allPosts.length / POSTS_PER_PAGE); const posts = getPaginatedPosts(allPosts, currentPage, POSTS_PER_PAGE); // 渲染文章预览 const postPreviews = posts.map((post) => ( <div key={post.slug} style={{ marginBottom: "1.5rem" }}> <h3>{post.data.article_body.content.heading.t_title}</h3> </div> )); // 渲染分页控件 const paginationControls = ( <div style={{ marginTop: "2rem", display: "flex", gap: "1rem", alignItems: "center" }}> {currentPage > 1 && ( <Link href={`/blog/${currentPage - 1}`} style={{ padding: "0.5rem 1rem", border: "1px solid #eee" }}> 上一页 </Link> )} <span>第 {currentPage} / {totalPages} 页</span> {currentPage < totalPages && ( <Link href={`/blog/${currentPage + 1}`} style={{ padding: "0.5rem 1rem", border: "1px solid #eee" }}> 下一页 </Link> )} </div> ); return ( <div style={{ maxWidth: "800px", margin: "2rem auto", padding: "0 1rem" }}> {postPreviews} {paginationControls} </div> ); }
关键修改说明
- 函数拆分:将原
getPostMetadata拆分为两个函数,避免重复读取文件,同时让分页逻辑更清晰 - 总页数计算修复:原代码中用当前页文章数计算总页数的逻辑错误,现在直接基于所有文章数计算
- 客户端导航优化:用Next.js的
Link组件替换原生a标签,实现无刷新页面跳转 - SEO优化:添加
generateMetadata函数为每个分页页面生成独立标题 - 静态参数生成:
generateStaticParams正确生成所有分页页面的静态路由参数,确保SSG正常生成所有页面
内容的提问来源于stack exchange,提问作者manggae
相关产品推荐
相关产品推荐

