如何在独立GitHub仓库存储博客数据并接入Next.js网站?
实现独立GitHub仓库存储博客数据并在Next.js中调用的可行方案
下面是几种成熟的实现方式,按需选择:
方式一:静态生成(SSG)+ GitHub API
适合追求页面加载速度、博客更新频率不高的场景,Next.js在构建时拉取GitHub仓库的博客数据,生成静态页面。
步骤:
- 博客数据仓库结构:在独立GitHub仓库中,用Markdown文件存储每篇博客,比如放在
posts/目录下,文件命名为yyyy-mm-dd-slug.md,文件内包含frontmatter(标题、日期等元数据)和正文:
--- title: "我的第一篇博客" date: "2024-05-20" tags: ["Next.js", "GitHub"] --- 这里是博客正文...
- Next.js项目配置:
- 安装依赖:
npm install @octokit/rest gray-matter react-markdown - 在页面组件中使用
getStaticProps拉取并解析数据:import { Octokit } from "@octokit/rest"; import matter from "gray-matter"; import ReactMarkdown from "react-markdown"; export async function getStaticProps() { const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN, // 私有仓库需配置,公开仓库可省略 }); // 获取posts目录下的所有文件 const { data: files } = await octokit.rest.repos.getContent({ owner: "你的GitHub用户名", repo: "博客数据仓库名", path: "posts", }); // 过滤Markdown文件并解析内容 const posts = await Promise.all( files .filter(file => file.name.endsWith(".md")) .map(async (file) => { const { data: content } = await octokit.rest.repos.getContent({ owner: "你的GitHub用户名", repo: "博客数据仓库名", path: file.path, mediaType: { format: "raw" }, // 获取原始文本内容 }); const { data: frontmatter, content: postContent } = matter(content); return { slug: file.name.replace(".md", ""), ...frontmatter, content: postContent, }; }) ); return { props: { posts }, revalidate: 86400, // 可选:24小时自动重新生成页面,实现增量更新 }; } export default function Blog({ posts }) { return ( <div className="blog-container"> <h1>我的博客</h1> {posts.map(post => ( <article key={post.slug}> <h2>{post.title}</h2> <p className="post-date">{post.date}</p> <ReactMarkdown>{post.content}</ReactMarkdown> </article> ))} </div> ); }
- 安装依赖:
方式二:API路由中间层 + 动态获取
适合需要动态过滤、搜索博客,或者不想在构建时拉取数据的场景,通过Next.js的API路由作为中间层,前端调用API获取数据。
步骤:
创建API路由:在
pages/api/posts.js中编写接口逻辑:import { Octokit } from "@octokit/rest"; import matter from "gray-matter"; export default async function handler(req, res) { try { const octokit = new Octokit(); const { data: files } = await octokit.rest.repos.getContent({ owner: "你的GitHub用户名", repo: "博客数据仓库名", path: "posts", }); const posts = await Promise.all( files .filter(file => file.name.endsWith(".md")) .map(async (file) => { const { data: content } = await octokit.rest.repos.getContent({ owner: "你的GitHub用户名", repo: "博客数据仓库名", path: file.path, mediaType: { format: "raw" }, }); const { data: frontmatter, content: postContent } = matter(content); return { slug: file.name.replace(".md", ""), ...frontmatter, content: postContent, }; }) ); res.status(200).json(posts); } catch (error) { res.status(500).json({ error: "获取博客数据失败" }); } }前端调用API:用SWR或React Query实现数据获取与缓存:
import useSWR from "swr"; import ReactMarkdown from "react-markdown"; const fetcher = url => fetch(url).then(res => res.json()); export default function Blog() { const { data: posts, error } = useSWR("/api/posts", fetcher); if (error) return <div>加载失败,请稍后重试</div>; if (!posts) return <div>加载中...</div>; return ( <div className="blog-container"> <h1>我的博客</h1> {posts.map(post => ( <article key={post.slug}> <h2>{post.title}</h2> <p className="post-date">{post.date}</p> <ReactMarkdown>{post.content}</ReactMarkdown> </article> ))} </div> ); }
关键注意事项
- 私有仓库访问:如果博客仓库是私有,需要生成GitHub个人访问令牌(PAT),勾选
repo权限,将令牌存在Next.js的.env.local文件中(GITHUB_TOKEN=你的令牌)。 - API限流:GitHub API有请求次数限制,公开仓库每小时60次,私有仓库每小时5000次,通过缓存(ISR、SWR)可有效减少调用次数。
- 扩展功能:可添加
remark-code-blocks实现代码高亮,或用remark-gfm支持GFM语法。
内容的提问来源于stack exchange,提问作者Ansub
相关产品推荐
相关产品推荐

