系列 · 这个博客是怎么搭起来的 第 2 篇 / 共 3 篇 实践

从代码看这个博客:项目结构与运行流程

从项目目录、页面入口、文章读取、组件渲染、本地运行和 GitHub Pages 发布链路,拆解这个 Astro 博客是怎么跑起来的。

作者:黄撑 更新于 2026-06-30

本文结论:这个博客不是传统后端网站,也不是 Vue 那种纯前端应用。它是一个 Astro 静态博客项目,代码在构建阶段把文章、页面、组件和样式组合起来,最后生成可以直接部署到 GitHub Pages 的静态文件。

理解这个项目,不需要先看所有代码。先抓住四个核心目录就够了:页面在 src/pages,文章在 src/content/posts,组件在 src/components,整体布局和样式在 src/layoutssrc/styles

概览

页面入口 pages

文件路径决定网站路由。

内容来源 posts

文章统一放在内容集合里。

展示能力 MDX

正文可以插入 Astro 组件。

发布形态 Static

构建后发布静态文件。

项目结构

核心目录可以压缩成一张图:

项目结构
flowchart TD
  Root[personal-blog] --> Src[src]
  Root --> Templates[templates]
  Root --> Public[public]
  Root --> Config[astro.config.mjs]
  Root --> Package[package.json]
  Root --> Workflow[.github/workflows]
  Src --> Pages[pages 页面入口]
  Src --> Content[content 文章内容]
  Src --> Components[components 展示组件]
  Src --> Layouts[layouts 页面外壳]
  Src --> Styles[styles 全局样式]
  Src --> Data[data 站点数据]
  Src --> Utils[utils 工具函数]

看懂这些目录,基本就能理解这个博客的代码如何组织。

Routes

src/pages

页面入口目录,决定首页、文章列表、详情页、项目页和关于页。

  • index.astro 是首页
  • articles/[…slug].astro 是文章详情
Content

src/content

内容集合目录,主要存放博客文章和 Starlight 指南内容。

  • posts/ 存文章
  • content.config.ts 定义字段规则
UI

src/components

可复用展示组件,文章和页面都可以调用。

  • 卡片、网格、高亮框
  • Mermaid 图表组件
Theme

src/styles

全局样式目录,控制整体视觉、文章样式、代码块和主题效果。

  • global.css 是主要样式入口
  • 代码块字体也在全局样式中维护

如果只从写文章的角度看,最常接触的是 src/content/poststemplates/post.mdx。如果要改页面和视觉,就会进入 src/pagessrc/componentssrc/styles

代码怎么运行

本地运行入口在 package.json。它没有复杂脚本,核心命令很少:

{
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "test": "vitest run"
  }
}

本地开发顺序

Dev 先启动本地预览

运行 npm run dev,Astro 会启动开发服务,适合边改边看。

Test 再跑项目测试

运行 npm test,确认现有测试没有被文章或代码改动破坏。

Build 最后执行静态构建

运行 npm run build,确认文章、MDX 组件和页面都能被正常生成。

这里要注意一点:npm run dev 只是本地预览,真正上线前更关键的是 npm run build。因为 GitHub Pages 托管的是构建后的静态文件,不是开发服务。

页面入口

Astro 的路由来自 src/pages 文件路径。这个项目没有单独维护一份复杂路由表,页面文件本身就是入口。

src/pages/index.astro
src/pages/articles/index.astro
src/pages/articles/[...slug].astro
src/pages/projects.astro
src/pages/about.astro
src/pages/rss.xml.ts
/

首页

读取公开文章和项目数据,展示首页导览、精选文章和项目记录。

/articles/

文章列表

读取公开文章,生成分类、标签和文章卡片列表。

/articles/[slug]/

文章详情

根据文章 slug 生成详情页,并渲染正文和目录。

这种结构的好处是直观:想找某个页面从哪里来,直接按 URL 去 src/pages 找对应文件。

文章怎么读取

文章放在 src/content/posts/,每篇文章开头都有 frontmatter。字段规则由 src/content.config.ts 定义:

const posts = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    date: z.coerce.date(),
    category: z.enum(['生活', '实践', '教程', '视觉实验室']),
    tags: z.array(z.string()),
    minutes: z.number(),
    featured: z.boolean().default(false),
    draft: z.boolean().default(false),
    private: z.boolean().default(false),
  }),
});

这段代码相当于文章的入库规则。标题、摘要、分类、标签、阅读时间这些字段不是随便写的,构建时会按 schema 检查。

文章怎么展示

文章列表页的核心逻辑很短:

const posts = (await getCollection('posts'))
  .filter((post) => !post.data.draft && !post.data.private)
  .sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());

它做了三件事:读取文章集合,过滤草稿和私密文章,再按日期倒序排列。

文章展示流程
flowchart LR
  A[MDX 文章] --> B[frontmatter]
  B --> C[content.config.ts 校验]
  C --> D[getCollection 读取]
  D --> E{是否公开}
  E -->|draft/private| F[不进入公开页面]
  E -->|公开| G[文章列表]
  G --> H[文章详情]
  G --> I[分类页]
  G --> J[标签页]
  G --> K[RSS]

文章公开后,会进入列表、详情、分类、标签和 RSS 等入口。

所以新增文章的流程很稳定:创建 MDX 文件,填好 frontmatter,确认发布后设为公开,再让页面代码自动读取。

详情页怎么生成

文章详情页在 src/pages/articles/[...slug].astro。这个文件使用 getStaticPaths() 为每篇公开文章生成静态路径:

export async function getStaticPaths() {
  const posts = (await getCollection('posts')).filter((post) => !post.data.draft && !post.data.private);

  return posts.map((post) => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

然后详情页再渲染正文:

const { Content, headings } = await render(post);

这里的 Content 是文章正文,headings 是文章目录。也就是说,文章页并不是访问时再去数据库查询,而是在构建阶段提前生成。

组件怎么工作

这个博客支持 MDX,所以文章不是只能写普通 Markdown。需要视觉笔记效果时,可以在文章顶部引入 Astro 组件:

import MetricCard from '../../components/MetricCard.astro';
import VisualGrid from '../../components/VisualGrid.astro';
import Mermaid from '../../components/Mermaid.astro';
指标

MetricCard

展示场景、输入、输出、风险、耗时等高扫描信息。

模块

FeatureCard

解释一个功能、目录、步骤或能力边界。

图表

Mermaid

把架构、流程和决策关系画成图,减少大段文字。

这也是这个博客文章风格的关键:内容还是文件,展示可以组件化。文章能被 Git 管理,也能被 Agent 修改和审查。

布局和样式

页面整体外壳在 src/layouts/SiteLayout.astro,全局样式在 src/styles/global.css

可以这样理解:

页面负责放什么
布局负责包一层什么结构
样式负责看起来是什么风格
组件负责复用局部展示能力

例如导航、页面宽度、文章容器、代码块、卡片视觉、明暗主题和字体规则,都不是写在每篇文章里,而是尽量沉到布局和全局样式里。

发布流程

本地和线上是两条链路。

运行与发布流程
flowchart TD
  A[修改文章或代码] --> B[npm run dev]
  B --> C[本地预览]
  A --> D[npm test]
  D --> E[npm run build]
  E --> F[git commit]
  F --> G[git push main]
  G --> H[GitHub Actions]
  H --> I[Astro 静态构建]
  I --> J[GitHub Pages]
  J --> K[线上博客]

本地用于预览和验证,线上由 GitHub Actions 构建并发布。

线上访问时,不需要 Node 服务一直运行。GitHub Actions 会把源码构建成静态产物,再交给 GitHub Pages 托管。

为什么适合 Agent 协作

这个项目对 Agent 友好,不是因为它复杂,而是因为规则显式、文件位置固定、验证命令明确。

Read

入口明确

Agent 先读 AGENTS.md 和 llms.txt,再根据任务读 docs/writing/ 下的规则、索引和模板。

  • 不用默认通读所有文章
  • 减少上下文浪费
Write

边界明确

文章字段、分类、草稿状态和发布规则都写在项目里。

  • 新增文章可审查
  • 发布前可验证

对个人博客来说,这种结构比一开始就做动态后台更轻。文章变更有 Git 记录,页面生成能构建验证,发布链路也足够简单。

检查清单

总结成一句话:Astro 读取文章和页面代码,在构建阶段生成静态网站,再由 GitHub Pages 托管。这个博客的代码重点不是功能堆叠,而是结构清楚、运行链路短、发布边界明确。