从零搭建个人博客:Next.js + Cloudflare Pages 全流程教程

Twitter card.jpeg
Published on
/
8 mins read
/
––– views

前言

一直想搭一个自己的博客,折腾了一番之后终于上线了。这篇文章记录整个过程,从选型到部署,包括中间踩过的坑,希望能帮到有同样想法的人。

技术选型

最终选择的方案:

组件选择理由
框架Next.js 15React 生态,SSG 静态导出,性能好
样式Tailwind CSS原子化 CSS,开发效率高
内容MDX + ContentlayerMarkdown 写作,支持 React 组件
部署Cloudflare Pages免费额度大,全球 CDN,速度快
域名Cloudflare DNS和 Pages 无缝集成

为什么不用 Vercel?Cloudflare Pages 免费额度更大(无限带宽、每月 500 次构建),而且域名本身就托管在 Cloudflare,配置更方便。

第一步:Fork 开源项目

博客基于 mengke.me 二次开发。这是一个功能很完善的 Next.js 博客模板,自带:

  • MDX 文章系统(代码高亮、数学公式、GFM)
  • 暗色模式
  • 全文搜索(Ctrl+K)
  • RSS 订阅
  • SEO 优化
  • 标签分类 + 分页

直接 Fork 到自己的 GitHub 账号,然后 clone 到本地:

git clone https://github.com/你的用户名/你的仓库名.git
cd 你的仓库名
pnpm install

第二步:个性化配置

需要修改的核心文件:

个人信息

编辑 data/author-info.ts

export const AUTHOR_INFO = {
  name: '你的名字',
  description: '一句话介绍',
  email: 'your@email.com',
  identity: 'Student | Developer',
  address: {
    city: 'Your City, Country',
    flag: 'flag-china', // Twemoji 国旗
    timeZone: 8,
  },
  work: {
    company: '',
    occupation: 'Student',
    location: 'Your City',
    website: '/',
  },
  social: {
    github: 'https://github.com/你的用户名',
    // 其他社交链接按需填写
  },
}

网站元数据

编辑 data/site-metadata.ts

export const SITE_METADATA = {
  title: '你的博客标题',
  author: '你的名字',
  siteUrl: 'https://你的域名',
  siteRepo: 'https://github.com/你的用户名/你的仓库',
  // ...
}

替换图片

  • public/static/images/logo.webp — 左上角 logo
  • public/static/images/profile.webp — 个人卡片头像

推荐用 webp 格式,体积小加载快。可以用在线工具转换,或者用 sharp-cli:

npx sharp-cli -i 你的图片.jpg -o public/static/images/profile.webp --format webp

关于我页面

编辑 data/authors/default.mdx,用 Markdown 写你的自我介绍。

第三步:适配静态导出

原项目可能是为 Vercel 设计的,部署到 Cloudflare Pages 需要改成静态导出模式。

修改 next.config.js

确保有这一行:

const output = 'export'

处理动态路由

output: 'export' 模式下,所有动态路由必须有 generateStaticParams()。如果某个动态路由(比如 app/snippets/[...slug]/)没有任何内容,Next.js 15 会报错:

Page "/snippets/[...slug]" is missing "generateStaticParams()"
so it cannot be used with "output: export" config.

解决方案:暂时删除没有内容的动态路由目录,等有内容了再加回来。

处理 fetch 缓存

如果代码中有 revalidate: 60 这样的增量静态再生成配置,需要改成 cache: 'no-store' 或直接删除,因为静态导出不支持 ISR。

第四步:本地测试

pnpm dev    # 开发模式预览
pnpm build  # 构建测试,确保没有报错

构建成功后会在 out/ 目录生成所有静态文件。

第五步:部署到 Cloudflare Pages

创建 Pages 项目

  1. 登录 Cloudflare Dashboard
  2. 左侧菜单 → Workers 和 Pages
  3. 点击 创建 → 选择 Pages 标签页(注意不要选 Workers)
  4. 点击 连接到 Git → 选择你的 GitHub 仓库

填写构建配置

配置项
生产分支main
框架预设Next.js (Static HTML Export)
构建命令pnpm build
构建输出目录out
部署命令true

踩坑提醒:部署命令必须填 true,不能留空(留空会报 Invalid request body),也不能填 npx wrangler deploy(那是 Workers 项目用的)。

添加环境变量

展开"环境变量",添加:

变量名
NODE_VERSION20

保存并部署

点击"保存并部署",等待 2-3 分钟。构建成功后 Cloudflare 会分配一个 xxx.pages.dev 的临时域名。

第六步:绑定自定义域名

  1. 进入 Pages 项目 → 自定义域
  2. 添加你的域名(如 blog.example.com
  3. 如果域名已经在 Cloudflare DNS 托管,会自动添加 CNAME 记录
  4. 等待几分钟 DNS 生效

踩坑记录

坑 1:pnpm-lock.yaml 不同步

现象:构建时报 ERR_PNPM_OUTDATED_LOCKFILE

原因:修改了 package.json 的依赖但没有重新生成 lockfile。Cloudflare Pages 默认用 --frozen-lockfile 安装。

解决

pnpm install
git add pnpm-lock.yaml
git commit -m "fix: sync pnpm-lock.yaml"
git push

坑 2:创建成了 Workers 项目

现象:部署成功但访问只显示 "Hello World"

原因:在 Cloudflare 创建项目时选成了 Workers 而不是 Pages。Workers 项目没有"构建输出目录"的概念。

解决:删除 Workers 项目,重新用 Pages 方式创建。区分方法:Pages 项目设置里有"构建输出目录"字段,Workers 项目有"兼容日期"、"Workers 日志"等字段。

坑 3:空的 catch-all 路由报错

现象Page "/snippets/[...slug]" is missing "generateStaticParams()"

原因:即使写了 generateStaticParams(),如果返回空数组,Next.js 15 在 output: 'export' 下对 catch-all 路由 [...slug] 也会报错。

解决:暂时删除没有内容的动态路由目录。

坑 4:部署命令填错

现象Missing entry-point to Worker script or to assets directory

原因:部署命令填了 npx wrangler deploy,这是 Workers 的部署命令,Pages 不需要。

解决:部署命令改成 true

后续可选功能

博客上线后,还可以按需配置这些功能:

  • Giscus 评论 — 基于 GitHub Discussions,免费无广告
  • Umami 分析 — 隐私友好的网站统计
  • 浏览量统计 — 用 Cloudflare Workers + KV 实现
  • Spotify 正在播放 — 用 CF Worker 代理 Spotify API

这些都是可选的,不配置也不影响博客正常运行。

自动部署

配置好之后,每次往 main 分支推送代码,Cloudflare Pages 都会自动构建和部署。写新文章只需要:

  1. data/blog/ 目录下新建 .mdx 文件
  2. 写好 frontmatter 和正文
  3. git push 到 GitHub
  4. 等 1-2 分钟自动上线

甚至可以直接在 GitHub 网页上创建文件,不需要本地环境。

总结

整个过程下来,最花时间的其实是踩坑。技术选型和配置本身不复杂,但 Cloudflare Pages 的一些细节(比如部署命令不能留空、Pages 和 Workers 的区别)文档里不太明显,需要自己摸索。

希望这篇教程能帮你少走一些弯路。如果有问题,欢迎在评论区交流。