这个博客是怎么搭起来的

终于把博客搭起来了。这篇记录一下整套技术选型和实际踩到的坑,省得以后重装环境时再翻一遍。

为什么是纯静态

博客这种东西,内容变更频率低、读者分散在全球各地,本质上就是一个「把 Markdown 渲染成 HTML 然后分发出去」的问题。

一旦接受这个前提,动态博客(WordPress 那类,PHP + 数据库)就很明显是过度设计了:

  • 要维护运行环境和数据库备份
  • 要操心安全补丁
  • 服务器挂了站点就挂了
  • 为了防住爬虫还得配缓存和 WAF

静态站点把这些全部消掉——构建产物就是一堆 HTML 文件,扔到 CDN 上,没有后端可攻击,没有数据库可炸。

技术栈

层 选型 理由
框架 Astro 默认零 JS,内容集合做类型校验
内容 Markdown / MDX 纯文本,好迁移,好做版本管理
搜索 Pagefind 构建时生成索引,无后端
评论 Giscus 基于 GitHub Discussions,无数据库
托管 Cloudflare Pages 免费、全球 CDN、自动 HTTPS

Astro 的内容集合

Astro 5 之后内容集合改成了 loader 模式,schema 里可以拿到 image() 做图片优化:

import { defineCollection } from 'astro:content';
import { z } from 'astro/zod';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
  schema: ({ image }) =>
    z.object({
      title: z.string(),
      pubDate: z.coerce.date(),
      heroImage: image().optional(),
    }),
});

注意几个点:

  1. z 要从 astro/zod 引,从 astro:content 引虽然还能用但已经是废弃写法
  2. image() 不是 import 进来的,是 schema 函数的上下文参数
  3. 渲染方式也变了:const { Content } = await render(entry),不是以前那种 entry.render()

踩到的坑

GitHub 从这台服务器完全不连通,create-astro 的模板校验全部超时。

官方脚手架 npm create astro@latest 无论选哪个模板都会去 GitHub 拉校验,在这台机器上直接 ECONNRESET。最后的解法是跳过脚手架手写项目结构,依赖全部从 npm registry 装——registry 是通的,所以这条路完全可行。

另一个坑是中文标签的 URL 编码:

// getStaticPaths 里必须先编码,否则中文路径会 404
params: { tag: encodeURIComponent(tag) }

部署流程

日常写作的循环就三步:

npm run dev      # 本地写,热更新
npm run build    # 生成 dist/ 并跑 Pagefind 建索引
npm run deploy   # 推到 Cloudflare Pages

没有 CI、没有服务器、没有需要守护的进程。构建产物推上去,全球 300+ 节点几分钟内就是最新的。

目前先这样跑着,等文章多起来再考虑接 Git 集成自动部署。

← 返回文章列表

评论