Next.js 个人博客 — 开发与部署指南
从零搭建一个基于 Next.js 的静态技术博客,通过 Cloudflare Pages 免费部署上线。本文涵盖技术架构、项目结构、开发流程、部署配置等完整指南。
项目概述
本项目是一个基于 Next.js 的静态个人技术博客,通过 Cloudflare Pages 免费部署上线。采用 Markdown 写文章、next build 生成纯静态 HTML、wrangler 推送到 Cloudflare 全球 CDN 的架构,实现了零服务器成本、自动 HTTPS、国内可访问的完整博客体验。
技术架构与原理
整体工作流程如下:
编写 Markdown 文章
↓
Next.js 静态构建 (next build + output: "export")
↓
生成纯 HTML/CSS/JS 到 out/ 目录
↓
wrangler pages deploy → Cloudflare 全球 CDN
↓
用户访问 *.pages.dev
为什么选择静态导出? Next.js 的 output: "export" 模式会在构建时把所有页面预渲染为独立的 HTML 文件,不依赖 Node.js 服务器运行。这意味着它可以部署到任何静态托管平台(Cloudflare Pages、GitHub Pages、Netlify 等),零运维成本。
为什么用 Cloudflare Pages? 相比 Vercel 的 .vercel.app 域名在国内几乎无法访问的问题,Cloudflare 在中国大陆有 CDN 节点,.pages.dev 域名通常可以直接打开,且免费套餐完全满足个人博客需求。
技术栈
| 层面 | 技术 | 说明 |
|---|---|---|
| 框架 | Next.js 16 + React 19 | App Router 架构,支持 SSG |
| 样式 | Tailwind CSS 4 | 原子化 CSS,暗色科技风主题 |
| 主题切换 | next-themes | 亮色/暗色自动切换,class 策略 |
| 文章引擎 | gray-matter + react-markdown | 解析 Markdown frontmatter + 渲染正文 |
| 代码高亮 | rehype-highlight | 基于 highlight.js 的语法高亮 |
| Markdown 增强 | remark-gfm | 支持表格、任务列表、删除线等 |
| 图标 | lucide-react | 轻量 SVG 图标库 |
| 部署 | Cloudflare Pages + wrangler | 全球 CDN,免费 |
| CI/CD | GitHub Actions | 推送代码自动部署 |
项目结构
myblog/
├── content/posts/ # Markdown 博客文章(核心内容目录)
│ ├── hello-world.md
│ ├── nextjs-performance-optimization.md
│ └── react-hooks-best-practices.md
├── src/
│ ├── app/ # Next.js App Router 页面
│ │ ├── layout.tsx # 根布局(字体、ThemeProvider、侧边栏)
│ │ ├── page.tsx # 首页(文章列表)
│ │ ├── HomeContent.tsx # 首页客户端组件(标签筛选交互)
│ │ ├── globals.css # 全局样式 + 暗色主题变量
│ │ ├── blog/[slug]/ # 文章详情页(动态路由)
│ │ │ └── page.tsx
│ │ └── resume/ # 简历页
│ │ ├── page.tsx
│ │ └── PrintButton.tsx
│ ├── components/ # 可复用 UI 组件
│ │ ├── Sidebar.tsx # 左侧边栏(头像、简介、导航)
│ │ ├── PostCard.tsx # 文章卡片
│ │ ├── TagFilter.tsx # 标签筛选
│ │ ├── TableOfContents.tsx # 文章目录
│ │ ├── ThemeToggle.tsx # 主题切换按钮
│ │ └── ...
│ └── lib/
│ └── posts.ts # 文章读取/解析/排序工具函数
├── public/ # 静态资源
├── .github/workflows/ # GitHub Actions 自动部署
│ └── deploy.yml
├── next.config.ts # Next.js 配置(静态导出)
└── package.json
本地开发
环境要求
- Node.js >= 22
- npm >= 10
初始化项目
git clone https://github.com/YinxuanLuo/nextjs-boilerplate.git
cd nextjs-boilerplate
npm install
启动开发服务器
npm run dev
浏览器访问 http://localhost:3000,支持热更新——修改代码或文章后页面自动刷新。
常用命令
npm run dev # 启动开发服务器(热更新)
npm run build # 生产构建,静态文件输出到 out/ 目录
npm run lint # 代码规范检查
写博客文章
创建新文章
在 content/posts/ 目录下新建 .md 文件,文件名即为 URL 路径(slug)。例如:
文件路径: content/posts/my-new-post.md
访问地址: https://myblog-3rz.pages.dev/blog/my-new-post/
文章格式
每篇文章由 frontmatter(元数据) + Markdown 正文 两部分组成:
---
title: "文章标题"
date: "2026-06-10"
summary: "一句话描述文章内容"
tags: ["React", "前端"]
draft: false
---
## 第一个标题
正文内容,支持完整的 Markdown 语法...
\`\`\`tsx
function Hello() {
return <h1>Hello World</h1>;
}
\`\`\`
frontmatter 字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | string | 是 | 文章标题,显示在详情页和卡片上 |
| date | string | 是 | 发布日期,格式 YYYY-MM-DD,用于排序 |
| summary | string | 是 | 文章摘要,显示在首页卡片上 |
| tags | string[] | 否 | 标签数组,用于分类筛选 |
| draft | boolean | 否 | 设为 true 时文章不会出现在列表中 |
文章解析原理
src/lib/posts.ts 中的核心流程:
fs.readdir扫描content/posts/下所有.md文件gray-matter解析 frontmatter(---之间的 YAML 部分)和正文reading-time根据正文字数计算阅读时长- 按日期降序排列,返回文章元数据数组
文章详情页通过 react-markdown + remark-gfm + rehype-highlight 将 Markdown 渲染为带样式的 HTML,自定义渲染器为标题添加锚点、为代码块添加语言标签和语法高亮。
构建与部署
构建配置
next.config.ts 是整个部署方案的核心:
const nextConfig: NextConfig = {
output: "export", // 静态导出模式
trailingSlash: true, // URL 尾部加 /,确保子目录有 index.html
images: {
unoptimized: true, // 静态导出不支持 next/image 优化
},
};
output: "export" 的原理: Next.js 在构建时会遍历所有路由,把每个页面预渲染成独立的 HTML 文件。比如 /blog/hello-world/ 会生成 out/blog/hello-world/index.html。这些文件可以直接被任何静态服务器托管,不需要 Node.js 运行时。
trailingSlash: true 的作用: 让 Next.js 把每个路由输出为 目录/index.html 而非 文件.html。Cloudflare Pages 对目录型路由的解析更友好,访问 /blog/hello-world/ 时会自动找到 blog/hello-world/index.html。
手动构建与预览
npm run build # 构建,输出到 out/ 目录
npx serve out # 本地预览静态文件
手动部署到 Cloudflare Pages
前置条件:安装 wrangler 并登录。
npm install -g wrangler # 安装 Cloudflare CLI
wrangler login # 浏览器授权登录
wrangler pages project create myblog --production-branch main # 首次创建项目
wrangler pages deploy out --project-name=myblog --branch=main --commit-dirty=true # 部署
部署原理: wrangler pages deploy 会把 out/ 目录下的所有文件上传到 Cloudflare 的边缘网络。Cloudflare 自动为 *.pages.dev 域名配置 HTTPS 证书和全球 CDN 加速。每次部署会生成唯一的部署 URL,同时 production 分支会更新主域名。
自动部署(GitHub Actions)
推送代码到 GitHub 后自动触发构建和部署,无需手动运行 wrangler。
工作流程
git push → GitHub Actions 触发
↓
checkout 代码 → 安装依赖 → npm run build
↓
cloudflare/wrangler-action 部署 out/ 到 Cloudflare Pages
↓
线上自动更新
配置文件
.github/workflows/deploy.yml:
name: Deploy to Cloudflare Pages
on:
push:
branches: [master]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: pages deploy out --project-name=myblog --branch=main --commit-dirty=true
配置步骤
第一步:创建 Cloudflare API Token
- 打开 https://dash.cloudflare.com/profile/api-tokens
- 点击 Create Token → 选择 "Edit Cloudflare Workers" 模板
- 权限确认包含:
Cloudflare Pages: Write、Account Settings: Read - 资源选择你的账号,创建并复制 Token
第二步:设置 GitHub Secret
- 打开仓库的 Settings → Secrets and variables → Actions
- 点击 New repository secret
- Name 填
CLOUDFLARE_API_TOKEN,Value 粘贴 Token - 保存
配置完成后,每次向 master 分支推送代码,GitHub Actions 会自动构建并部署到 Cloudflare Pages。
日常开发工作流
# 1. 写新文章:在 content/posts/ 下新建 .md 文件
# 2. 本地预览
npm run dev
# 3. 确认无误后提交代码
git add .
git commit -m "add: 新文章标题"
git push origin master
# 4. GitHub Actions 自动部署,几分钟后线上更新
自定义修改指南
| 想改什么 | 去哪里改 |
|---|---|
| 个人信息(名字、职位、简介) | src/components/Sidebar.tsx |
| 技能标签 | src/components/Sidebar.tsx 的 skills 数组 |
| 社交链接(GitHub、邮箱) | src/components/Sidebar.tsx 底部社交区域 |
| 主题颜色 | src/app/globals.css 的 CSS 变量 |
| 简历内容 | src/app/resume/page.tsx 的数据数组 |
| 首页欢迎文案 | src/app/page.tsx 的 header 部分 |
| 导航菜单 | src/components/NavLinks.tsx 的 navItems |
常见问题
为什么不用 Vercel 部署? Vercel 的 .vercel.app 域名在中国大陆被 DNS 污染,几乎无法访问。Cloudflare Pages 的 .pages.dev 域名在国内网络下通常可以正常打开。
静态导出有什么限制? 不支持需要服务器运行的功能:API Routes、getServerSideProps、next/image 自动优化、中间件等。但对博客这类内容站来说,纯静态完全够用,而且加载速度更快。
如何绑定自定义域名? 在 Cloudflare Dashboard → Pages → myblog → Custom domains 中添加域名。如果域名在 Cloudflare 管理,DNS 会自动配置;否则需要手动添加 CNAME 记录指向 myblog-3rz.pages.dev。
文章里的图片怎么放? 将图片放到 public/ 目录下,在 Markdown 中用 /图片名.png 引用。也可以直接使用外部图床链接。注意静态导出不支持 next/image 的自动优化。