part-8 · 发布与数字花园

Astro + Cloudflare 实战

从零搭建一个 Obsidian 笔记站点:Astro 内容集合、remark 语法兼容、基础功能与 Cloudflare Pages 部署全流程。

上一章我们比较了五条自建路线。这一章,我们把其中最灵活的一条——Astro——从空目录走到上线。你正在浏览的这个教程网站,本身就是这条流程的产物:Astro 编译 Markdown,Cloudflare Pages 全球分发,自定义域名配 HTTPS。下面是完整的施工图。

第一步:初始化项目

用 Astro 官方脚手架起步,选一个最小模板:

npm create astro@latest obsidian-garden -- --template minimal
cd obsidian-garden
npm install

Astro 会问你几个问题,按需选择即可。脚手架完成后,npm run dev 就能在本地 http://localhost:4321 预览。

第二步:配置内容集合

Astro 的 Content Collections 是管理 Markdown 笔记的核心。它让你给笔记定义一份 schema——哪些字段是必需的、什么类型——编译时就会校验,写错 frontmatter 立刻报错。

src/content/ 下新建 config.ts

import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string().optional(),
    pubDate: z.coerce.date(),
    part: z.string().optional(),
    order: z.number().optional(),
    author: z.string().optional(),
    tags: z.array(z.string()).optional(),
  }),
});

export const collections = { blog };

把笔记放进 src/content/blog/,Astro 就能用 getCollection('blog') 取出全部条目,按 pubDateorder 排序,按 part 过滤分章——这个教程的目录页正是这么生成的。

第三步:驯服 Obsidian 方言

Astro 默认只认 GitHub Flavored Markdown,[[双链]]![[]] 嵌入、> [!callout] 这些 Obsidian 方言它会原样吐出。补救办法是挂载 remark 插件,在 Markdown 被 parse 成 AST 之后、渲染成 HTML 之前做转换。

astro.config.mjs 里配置:

import { defineConfig } from 'astro/config';
import remarkObsidian from 'remark-obsidian'; // 或自写插件

export default defineConfig({
  markdown: {
    remarkPlugins: [remarkObsidian],
  },
});

社区有 remark-wiki-linkremark-obsidian-callout 等插件可组合使用,分别处理双链解析和 Callout 转换。思路一致:把 [[页]] 解析成正确的 <a href>,把 > [!note] 转成带 class 的 <div class="callout">,把 ![[图片]] 转成 <img>。如果现成插件不完全合用,写一个 remark 插件也不复杂——遍历 AST 节点、匹配语法、替换节点即可。

第四步:路由、布局与组件

Astro 的路由是文件即路由src/pages/index.astro 是首页,src/pages/blog/[...slug].astro 会为每篇笔记生成一个页面。

动态路由用 getStaticPaths 列出所有笔记:

---
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Post.astro';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map(post => ({
    params: { slug: post.slug },
    props: { post },
  }));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<Layout title={post.data.title}>
  <article>
    <h1>{post.data.title}</h1>
    <Content />
  </article>
</Layout>

Layout 组件负责统一的页头、页脚、侧边栏。暗色模式用一段读取 localStorage 的内联脚本,在页面首次绘制前就把 data-theme 写到 <html> 上,避免闪烁。目录(Table of Contents)可以从 Markdown 的 heading 列表自动生成。搜索则可以用 pagefind——一个专为静态站点设计的客户端搜索库,构建时索引、运行时零依赖。

第五步:部署到 Cloudflare Pages

代码就绪,接下来让它上线。Cloudflare Pages 的免费额度对个人站点绰绰有余,且自带全球 CDN 与边缘网络。

  1. 推送代码到 GitHub(或 GitLab)。
  2. 登录 Cloudflare Dashboard,进入 Workers & Pages → Create application → Pages → Connect to Git
  3. 选中你的仓库,填写构建配置:
    • Framework preset:选 Astro
    • Build commandnpm run build
    • Build output directorydist
    • Node version(环境变量):NODE_VERSION = 20
  4. Save and Deploy,Cloudflare 会拉取代码、运行构建、把 dist/ 部署到全球边缘节点。

此后每次推送到主分支,Cloudflare 自动触发新一轮构建;每个预览分支还会生成独立的预览 URL,方便上线前检查。

第六步:自定义域名与 HTTPS

在 Pages 项目的 Custom domains 标签页,点 Set up a custom domain,输入你的域名。如果域名 DNS 也在 Cloudflare 管理,它会自动添加 CNAME 记录;如果在他处,手动添加一条 CNAME 指向 your-project.pages.dev 即可。

HTTPS 是自动的——Cloudflare 签发并续期证书,无需你操心。几分钟后,https://你的域名 就能访问。开启 “Always Use HTTPS” 强制跳转,再配上 HSTS,安全等级拉满。

一条路,走通了

从空目录到上线,我们经历了六步:初始化、内容集合、语法兼容、布局组件、部署、域名。每一步都不复杂,组合起来却是一套完整、免费、完全可控的发布管线。

Astro 给了你造任何形状站点的自由,Cloudflare 给了它跑遍全球的速度。而 Obsidian 里的那些笔记——那些双链、嵌入、callout——经过 remark 插件的转译,终于在互联网上长成了一座可以被人漫游的花园。这,就是本教程网站自己的故事。