从 0 到 1 搭建这个博客:选型与部署全记录

本文是一篇完整的实战记录,分两部分:

  • 第一部分:完整的选型分析(为什么是 Astro + Cloudflare Pages)
  • 第二部分:从 0 到 1 的实际搭建与部署全过程(所有命令都在本机实测执行过)

全程由 Agent 在命令行完成,因此把每一步的命令和决策都原样记录,方便复现,也给自己留一份操作档案。


第一部分:选型 —— 为什么是 Astro + Cloudflare Pages

一、需求与约束

这次选型的核心目标不是单纯”把博客跑起来”,而是兼顾长期维护和服务器资源规划:

  • 拥有独立域名和可迁移的博客内容
  • 尽量少占用云服务器的常驻内存
  • 云服务器后续还要部署其他网站
  • 使用 Markdown 写作,并用 Git 管理内容
  • 支持现代化样式、深色模式和一定的交互能力
  • 不在第一版维护数据库、后台程序和复杂容器栈

在这些约束下,静态站点比 WordPress、Halo、Ghost 等动态 CMS 更合适。

二、静态站点到底”静态”在哪里

“静态”不是指页面不能动、不能搜索或不能评论,而是指:

访客访问页面时,托管平台直接返回提前生成好的 HTML、CSS 和 JavaScript 文件,不需要现场查询数据库并生成 HTML。

content/posts/hello.md
        ↓ Astro 构建
dist/posts/hello/index.html
        ↓ 访客访问
Cloudflare Pages 直接返回 index.html

页面仍可通过浏览器端 JavaScript 实现深色模式、搜索、图片灯箱、代码复制、动画、评论和阅读进度。

SSG、SSR、CSR 与 ISR

模式全称HTML 生成时机运行时要求典型场景
SSGStatic Site Generation构建、发布时提前生成Hugo、Hexo、Astro 静态模式
SSRServer-Side Rendering每次请求到达服务器时生成需要服务端或边缘运行时Next.js SSR、Astro Server 模式
CSRClient-Side Rendering浏览器下载 JavaScript 后生成通常不需要渲染服务React/Vue 单页应用
ISRIncremental Static Regeneration先生成,过期后按需更新需要平台或运行时支持内容量较大的现代网站

可以用做饭来类比:

SSG:客人来之前,饭已经做好
SSR:客人点单后,服务器现场做饭
CSR:服务器给食材,由客人的浏览器做饭

Astro 默认采用静态输出,因此本方案属于 SSG,而不是 SSR。

三、动态 CMS 与静态方案的初步筛选

方案主要优点主要代价是否符合本次目标
Halo中文后台、主题插件、Markdown/富文本编辑Java 应用和数据库长期占用内存一般
Ghost写作体验好,支持会员与 NewsletterNode.js、MySQL;使用邮件功能时还需 SMTP一般
WordPress插件和主题生态最大PHP、数据库、插件安全更新较重不优先
Hugo构建极快、依赖少、长期稳定中文主题和现代组件能力相对有限符合
Hexo中文博客生态强,Butterfly 等主题成熟npm 依赖较多,构建速度不如 Hugo符合
Astro静态输出、现代组件化、扩展空间最大相比传统博客生成器需要更多前端开发最符合

如果目标是登录后台直接写文章,Halo 更方便;如果目标是低资源占用、Git 管理和长期可迁移,静态方案更有优势。

四、Hugo、Hexo 与 Astro 对比

维度HugoHexoAstro
技术基础GoNode.jsNode.js / TypeScript
默认产物静态 HTML静态 HTML静态 HTML
线上常驻内存接近 0接近 0静态模式接近 0
构建速度最快中等中等
博客开箱程度很高中等
中文主题生态一般很强相对较少
UI 深度定制Hugo 模板主题、Pug、Stylus组件化开发,能力最强
依赖复杂度最低中等中等
适合人群内容与稳定性优先快速套成熟博客主题现代化、个性化网站

1. Hugo

Hugo 通常以单个可执行文件运行,不依赖庞大的 node_modules,适合追求构建速度和长期稳定的人。

  • 优点:构建速度快(文章很多时优势明显);依赖链短,升级和安全风险较低;静态产物容易部署到任何 Web 服务器。
  • 不足:中文主题和教程不如 Hexo 丰富;深度修改主题需要学习 Hugo 模板语法;现代前端组件和交互扩展不如 Astro 自然。

2. Hexo

Hexo 专门面向博客,中文生态成熟。

  • 优点:分类、标签、归档、友链等博客功能成熟;Butterfly 等中文主题开箱即用;中文教程和社区资料丰富。
  • 不足:npm 依赖和主题插件之间可能存在版本兼容问题;构建速度和依赖体积不如 Hugo;高度定制时容易进入主题”魔改”模式。

3. Astro

Astro 默认生成静态 HTML,也可以按需嵌入 React、Vue、Svelte 等交互组件。

  • 优点:默认 SSG,不需要线上 Node.js 常驻进程;可以只给需要交互的部分发送 JavaScript;TypeScript、组件化和现代 CSS 工具支持好;后续可从博客扩展到个人主页、作品集或交互页面;构建后的 dist/ 可部署到 Pages,也可迁回自己的服务器。
  • 不足:博客主题生态不如 Hexo;需要理解页面布局、内容集合和组件结构;如果以后切换到 SSR,需要额外的服务端、边缘或 Serverless 运行时;若部署在自有服务器,还会增加常驻内存开销。

4. 选择结论

  • 极致稳定、构建最快:Hugo
  • 想直接获得 Butterfly 风格:Hexo
  • 希望兼顾静态博客和未来前端扩展:Astro

最终选择 Astro。原因不是它比 Hugo、Hexo 更省线上内存——三者静态部署后几乎相同——而是 Astro 在保持静态部署的同时提供了更大的界面和功能扩展空间。

五、云服务器、GitHub Pages 与 Cloudflare Pages

生成器和托管平台属于不同层次:

Hugo / Hexo / Astro:负责生成网站
云服务器 / GitHub Pages / Cloudflare Pages:负责托管网站
维度自有云服务器GitHub PagesCloudflare Pages
费用已购买,但消耗资源和带宽免费免费额度足够个人博客
自定义域名支持支持支持
HTTPS自己配置 Caddy/Nginx自动自动
构建方式本地或 CI 构建后上传GitHub ActionsGit 集成自动构建
占用本机内存静态托管增量很低00
占用本机带宽
私有源码仓库自己决定免费账户的 Pages 能力受仓库类型限制可连接私有 GitHub 仓库
预览部署需要自己实现可通过 Actions 实现原生支持
动态扩展可自由部署后端基本没有可接 Workers / Pages Functions

为什么选择 Cloudflare Pages

  • 不消耗自己的 2C4G 服务器内存
  • 不占用服务器有限的 5 Mbps 带宽
  • Git 推送后自动构建、发布
  • 免费提供 HTTPS 和自定义域名
  • 可以连接 GitHub 私有仓库
  • 支持预览每次修改
  • 静态产物标准化,随时可以迁回云服务器

六、域名、子域名与镜像

1. 一个域名可以创建多个子域名

不需要为主站和镜像分别购买两个域名。购买一个 example.com 后,可以创建:

子域名用途指向
blog.example.com博客主站Cloudflare Pages
mirror.example.com博客镜像GitHub Pages 或云服务器
comment.example.com评论 API自有云服务器
www.example.com个人主页任意平台

子域名通常不需要额外购买。

2. 什么是博客镜像

博客镜像是同一份静态构建产物部署到多个地址:

同一份 Git 仓库
        ↓ 构建
       dist/
        ├── Cloudflare Pages 主站
        ├── GitHub Pages 镜像
        └── 自有云服务器镜像

镜像主要用于故障备用、比较访问质量和避免单个平台不可用。它不是 Docker 镜像,也不等于完整灾备;Git 仓库和文章源码仍应额外备份。

七、最终架构:Astro + Cloudflare Pages

本地编写 Markdown / MDX

提交到 GitHub 私有仓库

Cloudflare Pages 检测到 push

平台安装依赖并执行 npm run build

Astro 将内容构建到 dist/

Cloudflare 全球静态网络提供访问

blog.example.com + 自动 HTTPS

Cloudflare Pages 基本构建配置:

Build command: npm run build
Build output directory: dist

Astro 保持默认静态模式:

import { defineConfig } from 'astro/config'

export default defineConfig({
  output: 'static'
})

不要使用 output: 'server',除非未来确实需要请求时动态渲染,并愿意承担运行时服务和额外维护成本。

八、资源占用分析

1. 构建放在 Cloudflare 时

Astro 构建所需 CPU/内存:由 Cloudflare 承担
静态文件存储和流量:由 Cloudflare 承担
自己的云服务器内存:0
自己的云服务器带宽:0

这能把 2C4G 云服务器完整留给以后真正需要后端运行时和数据库的网站。

2. 如果以后迁回云服务器

只需将 dist/ 上传,通过一个共享的 Caddy 或 Nginx 进程托管多个目录:

Caddy/Nginx
├── blog.example.com   → /var/www/blog
├── site-a.example.com → /var/www/site-a
└── site-b.example.com → /var/www/site-b

多个静态站不需要分别启动多个 Node.js 服务,新增站点的常驻内存成本很低。

3. 容易被忽略的动态服务

功能方案是否占用自有服务器
Giscus(GitHub Discussions)
Gitalk(GitHub Issues)
Artalk 自托管
Waline 自托管
Astro 本地静态搜索
Meilisearch 等独立搜索

为保持轻量,第一版可使用 Giscus 或暂时不加评论,搜索使用构建时索引或浏览器端本地搜索。

九、第一版功能边界

第一版实现:

  • Markdown/MDX 文章
  • 首页、文章列表与文章详情
  • 标签或分类
  • 代码高亮与深色模式
  • RSS、Sitemap、SEO 和 Open Graph
  • 响应式布局
  • Cloudflare Pages 自动部署
  • 自定义域名与 HTTPS

后续再加:

  • Giscus 评论与全文搜索
  • 图片灯箱和阅读进度
  • 访问统计、友链、归档和作品集
  • Cloudflare R2 图片存储

暂不引入:

  • SSR、数据库和用户登录
  • 自建管理后台
  • 自建搜索集群
  • 复杂微服务或容器编排

十、实施清单

项目初始化

  • 创建 Astro 项目
  • 确认 output: 'static'
  • 规划 src/content 文章集合
  • 选定基础主题或页面风格
  • 建立 Markdown Frontmatter 规范

博客基础能力

  • 首页、文章列表与文章详情
  • 代码高亮、标签、分类或归档
  • RSS、Sitemap 和 404 页面
  • SEO、Canonical URL、Open Graph
  • 深色模式与移动端适配

部署

  • 源码推送到 GitHub 私有仓库
  • Cloudflare Pages 连接仓库
  • 构建命令设为 npm run build
  • 输出目录设为 dist
  • 绑定自定义子域名
  • 验证 HTTPS、重定向和预览部署

运维与备份

  • 本地保留 Git 仓库副本
  • 定期同步到第二个备份位置
  • 图片压缩为 WebP/AVIF
  • 不存放大视频和大量原图
  • 固定 Node.js 与依赖版本
  • 定期检查构建日志和失效链接

第二部分:从 0 到 1 的实际搭建过程

以下是本机实际执行的全过程,所有命令都可复现。

一、环境准备

本机已有 Node.js 和 npm(Node v24.19.0 / npm 11.17.0),Git 已配好 SSH 密钥访问 GitHub,网络走本地 Clash 代理(127.0.0.1:7890 / 7891)。

node -v        # v24.19.0
npm  -v        # 11.17.0
ls ~/projects  # 工作目录

二、创建 Astro 项目

用 Astro 官方 blog 模板(自带文章集合、RSS、sitemap、深色模式)。

cd ~/projects
npm create astro@latest blog -- --template blog --install --no-git --yes

生成的目录结构(关键部分):

blog/
├── astro.config.mjs          # 站点配置
├── package.json
├── src/
│   ├── content/blog/         # ▸ Markdown/MDX 文章存放处
│   ├── content.config.ts     # 内容集合 schema
│   ├── consts.ts             # 站点标题/描述
│   ├── layouts/BlogPost.astro
│   ├── pages/                # 首页、列表页、rss.xml.js
│   ├── styles/global.css
│   └── components/
└── public/                   # 静态资源

三、本地配置与定制

1. 确认静态模式(SSG)

astro.config.mjs 显式声明 output: 'static'

export default defineConfig({
	site: 'https://example.com',   // 之后替换成真实域名
	output: 'static',              // SSG:构建时生成静态 HTML,不部署 Node 常驻进程
	integrations: [mdx(), sitemap()],
});

2. 站点元信息

src/consts.ts 里改为本站标题和描述:

export const SITE_TITLE = '肖飞的博客';
export const SITE_DESCRIPTION = '记录 AI 与开发学习笔记的个人博客。';

3. 内容集合加标签字段

src/content.config.ts 的 schema 里加可选 tags 数组:

pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
tags: z.array(z.string()).optional(),
heroImage: z.optional(image()),

4. 列表页和文章页渲染标签

列表页 src/pages/blog/index.astro——每个文章卡片日期下方显示标签:

<p class="date">
	<FormattedDate date={post.data.pubDate} />
</p>
{post.data.tags && (
  <p class="tags">
    {post.data.tags.map((tag) => <span class="tag">#{tag}</span>)}
  </p>
)}

配套样式:

.tags { margin: 0.25rem 0 0; display: flex; gap: 0.5rem; flex-wrap: wrap; }
.tags .tag { font-size: 0.8rem; color: rgb(var(--gray)); background: rgba(var(--gray), 0.12); padding: 0.1rem 0.5rem; border-radius: 999px; }

文章页 src/layouts/BlogPost.astro——在标题下方同样显示标签(从 Props 解构 tags 即可)。

5. 给示例文章加标签

src/content/blog/first-post.md 的 frontmatter 里加上:

tags: ['astro', 'blog', '入门']

四、本地构建验证

cd ~/projects/blog
npm run build

输出在 dist/ 生成静态文件(首页、/blog/... 文章页、rss.xmlsitemap 等):

✓ Completed in 1.14s
[@astrojs/sitemap] `sitemap-index.xml` created at `dist`
[build] 8 page(s) built in 2.29s

用 grep 验证标签已渲染进产物:

grep -o 'class="tags"' dist/blog/index.html
grep -o '#astro' dist/blog/index.html   # → 找到标签

五、git 初始化与提交

cd ~/projects/blog
git init -b main
git add -A
git commit -m "init: Astro blog (static SSG) - content collections, tags, rss/sitemap"

六、安装 gh CLI 并登录 GitHub

为了在命令行创建仓库和推送,安装 GitHub CLI(gh),并走 OAuth 设备码授权登录(headless 环境的标准方式)。

安装 gh(二进制,免 sudo)

cd /tmp
VER=$(curl -s https://api.github.com/repos/cli/cli/releases/latest | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'].lstrip('v'))")
curl -sL -o gh.tgz "https://github.com/cli/cli/releases/download/v${VER}/gh_${VER}_linux_amd64.tar.gz"
tar xzf gh.tgz
mkdir -p ~/.local/bin
cp gh_${VER}_linux_amd64/bin/gh ~/.local/bin/
chmod +x ~/.local/bin/gh
export PATH="$HOME/.local/bin:$PATH"
gh --version   # gh version 2.98.0

设备码授权登录

调用 GitHub 的设备码接口拿到一次性代码,用户在浏览器打开 https://github.com/login/device 输入即可完成授权(授权范围:repo, read:org, gist)。授权成功后 gh 会把 token 写入 ~/.config/gh/hosts.yml

gh auth status
# ✓ Logged in to github.com account Starflyr
#   Git operations protocol: ssh

💡 提示:这台机器之前配的 GitHub 账号是 Xiao198,后来改名为 Starflyr(显示名 Starflyer,与域名 starflyer.top 呼应),所以登录账号是 Starflyr

七、创建私有仓库并推送

设置 git 提交身份与账号一致,然后一键创建私有仓库并推送:

git config --global user.name  "Starflyer"
git config --global user.email "Starflyr@users.noreply.github.com"

cd ~/projects/blog
gh repo create Starflyr/blog --private --source=. --remote=origin --push \
  --description "Personal blog built with Astro (static SSG)"

成功输出:

https://github.com/Starflyr/blog
To github.com:Starflyr/blog.git
 * [new branch]      HEAD -> main
branch 'main' set up to track 'origin/main'.

八、Cloudflare Pages 部署

  1. 登录 dash.cloudflare.comWorkers & PagesPages → Connect to Git

  2. 授权 Cloudflare 访问 GitHub,选择账号 Starflyr,选中仓库 blog

  3. 构建配置:

    Framework presetAstro
    Build commandnpm run build
    Build output directorydist
  4. Save and Deploy,Cloudflare 自动拉取仓库、构建、发布

⚠️ 小坑:第一次很容易点进 Workers(会要求 npx wrangler deploy,那是跑服务端代码的),静态博客必须选 Pages。Pages 的构建界面不会出现 Deploy command: npx wrangler deploy 这一行,这是两个入口最明显的区分。

部署完成得到地址:https://blog-8jx.pages.dev/blog 是项目名,8jx 是 Cloudflare 为全球唯一自动补的随机后缀)。

九、上线验证

# 关键页面 HTTP 状态
for u in "https://blog-8jx.pages.dev/" "https://blog-8jx.pages.dev/blog/first-post/" "https://blog-8jx.pages.dev/rss.xml"; do
  echo "$(curl -s -o /dev/null -w '%{http_code}' "$u")  $u"
done
# 全部 200

# 确认中文标题与编码
curl -s "https://blog-8jx.pages.dev/" | grep -io 'charset="?[a-z0-9-]*'
# charset=utf-8、title=肖飞的博客

# 确认标签渲染
curl -s "https://blog-8jx.pages.dev/blog/first-post/" | grep -o '#astro'
# → #astro

所有页面 HTTP 200,中文标题正常(utf-8),RSS / 标签 / 文章页全部跑通,博客正式上线。

十、后续规划

  • 📝 写第一篇文章(就是这篇)
  • 🏷 替换示例文章(first-post 等模板内容)
  • 🌐 starflyer.top 域名审核通过后,绑定 blog.starflyer.top 子域名 + HTTPS
  • 💬 加评论(Giscus)、全文搜索、访问统计
  • 🖼 图片灯箱、阅读进度、归档、友链、作品集

总结

Astro 静态模式(SSG)
        +
GitHub 私有仓库(Starflyr/blog)
        +
Cloudflare Pages 自动构建与托管
        +
一个自定义域名(starflyer.top,待审核)

这个方案全程没有占用云服务器的内存和带宽;用 Markdown 写作 + Git 管理,便于备份和迁移;dist/ 标准化产物随时可以迁回自有服务器。

最重要的是这句:生成器决定怎么制作网站,托管平台决定网站在哪里运行。

从 0 到 1 搭建这个博客:选型与部署全记录

作者

Starflyer

发布日期

2026 - 08 - 27

分类