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

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

沿 URL、Content Collections、MDX 渲染、静态生成与 GitHub Pages 发布链路,定位这个 Astro 博客的一次请求和构建。

作者:黄撑 更新于 2026-07-26
本文目录 6 节 · 点击展开

这个博客在线上没有常驻应用服务器:页面与文章在构建阶段组合成静态文件,访问时由 GitHub Pages 直接返回结果。理解代码时,不必通读仓库;沿一条 URL 反查到页面入口,再顺着内容、渲染和构建往下走即可。

RUNTIME BLUEPRINT · 02这一篇追踪代码,不重复系统架构

系列上一篇《这个博客是怎么搭起来的》解释仓库、内容边界、规则与发布责任;本篇只回答:一个 URL 或一次构建,究竟经过哪些文件和 Astro 能力。

  1. 01URL找到页面入口
  2. 02Content读取并校验文章
  3. 03Render组合 MDX 与布局
  4. 04Output生成静态文件

从 URL 反查:页面文件就是路由入口

R01 · ROUTE TABLE先把访问路径翻译成 src/pages 下的文件

Astro 使用文件路由;静态页面直接对应文件,方括号目录或文件名表示构建时生成的动态参数路径。

浏览器 URL页面入口构建职责
/src/pages/index.astro

当前转交给 home-redesign-1.astro 渲染首页。

/articles/src/pages/articles/index.astro

读取公开文章,组织分类、标签、排序与文章列表。

/articles/{slug}/src/pages/articles/[...slug].astro

getStaticPaths() 为每篇公开文章生成详情路径。

/articles/category/{category}/src/pages/articles/category/[category].astro

为固定分类生成聚合页。

/articles/tag/{tag}/src/pages/articles/tag/[tag].astro

从公开文章标签生成聚合路径。

/series/{series}/src/pages/series/[series].astro

按 series 元数据和文章顺序生成系列页。

/rss.xmlsrc/pages/rss.xml.ts

把公开文章映射为 RSS item。

LOCATE页面问题先从 URL 找 src/pages;不要先钻进组件或文章正文。

文章怎样进入 Content Collection

R02 · CONTENT INTAKEMarkdown / MDX 文件先通过 schema,再被页面读取、过滤和排序

src/content.config.ts 是文章字段的代码边界;分类与系列合法值分别来自 src/data/categories.tssrc/data/series.ts

SOURCEsrc/content/posts/**/*.{md,mdx}

正文与 frontmatter 一起进入 posts collection。

SCHEMAdefineCollection({ type: 'content' })

Zod 校验字段类型、默认值、合法分类与系列 ID。

QUERYgetCollection('posts')

页面、布局和 RSS 从同一个集合读取文章。

PUBLIC FILTER!draft && !private

公开入口统一排除草稿和私密文章。

IDENTITYtitle · description · date · updated?

标题、摘要与时间信息。

INDEXcategory · tags · minutes · featured

分类、检索、阅读时间与精选状态。

SERIESseries? · seriesOrder?

可选系列 ID 与正整数顺序。

BOUNDARYdraft · private

两个字段默认都是 false;公开页面同时过滤两者。

集合定义(节选)
const posts = defineCollection({
type: 'content',
schema: z.object({
  title: z.string(),
  category: z.enum(categories),
  draft: z.boolean().default(false),
  private: z.boolean().default(false),
}),
});
公开读取(实际模式)
const posts = (await getCollection('posts'))
.filter((post) => !post.data.draft && !post.data.private)
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());

schema 能确认字段是否合法,页面过滤决定内容是否进入公开入口。两者解决的是不同问题:前者阻止结构错误,后者实施发布边界。

一篇 MDX 详情页怎样完成渲染

R03 · RENDER STACK路径在构建期确定,正文再与组件、布局和全局样式合并

详情页入口是 src/pages/articles/[...slug].astro。它既负责生成路径,也负责渲染文章与系列导航。

L1 · PATH
getStaticPaths()

读取公开 posts,把每篇 post.slug 映射为静态路径和 props。

params: { slug: post.slug }
L2 · CONTENT
render(post)

返回正文组件 Content 与标题数据 headings

const { Content, headings } = await render(post);
L3 · MDX
Markdown + Astro components

MDX integration 让正文保留 Markdown,同时导入文章专属或现有 Astro 组件。

integrations: [mdx()]
L4 · LAYOUT
SiteLayout.astro + [...slug].astro

SiteLayout 提供导航、搜索、主题与站点外壳;文章路由组织侧栏、正文和深度为 2 或 3 的目录 headings。

<Content />
L5 · STYLE
src/styles/global.css + article CSS

全局文件控制站点外壳;单篇独有信息图用文章 class 限定,避免污染其他页面。

html[data-theme='dark']
BUILD RESULT每篇公开文章得到一个静态详情页

浏览器收到的是已经生成的 HTML、CSS 与必要脚本;不会在每次访问时重新查询文章数据库。

MDX 组件不是独立应用层。它们在构建时成为文章渲染树的一部分;页面外壳与全局主题仍由 SiteLayout 和 global.css 负责。

静态构建怎样把所有入口合成产物

R04 · BUILD FACTORY一次 build 同时处理页面路由、文章路径、组件依赖和公开资源

当前 astro.config.mjs 配置站点地址并启用 MDX,没有配置 base;输出沿 Astro 当前默认静态站点方式生成。

ROUTESsrc/pages/

固定入口与参数化入口

CONTENTposts collection

公开文章与 frontmatter

UIcomponents · layouts · styles

渲染能力与主题

ASSETSpublic/

按原路径复制的公开文件

ASTRO BUILD解析 → 校验 → 渲染 → 生成

Content schema 先检查文章;页面收集路径;MDX 与 Astro 组件编译;静态入口写入产物目录。

npm run build
HTML页面与文章
CSS / JS样式与必要交互
PUBLIC公开静态资源
RSSrss.xml
构建前错误frontmatter、MDX 语法、导入路径或页面代码可让 build 失败。
构建后边界产物用于托管;下一次内容修改仍应回到源码,而不是手改 dist/

本地运行和线上发布是两条不同链路

R05 · TWO ENVIRONMENTS本地命令验证源码,Actions 与 Pages 交付静态产物

开发服务可快速反馈,但上线前仍要以测试和静态构建为准;工作流实际运行和线上结果属于外部实时状态。

LOCAL · 开发与验收package.json
  1. Dev启动开发服务,边改边看。
  2. Test运行 Vitest 项目测试。
  3. Build执行正式静态构建。
  4. Preview预览已经生成的构建产物。
npm run dev
npm test
npm run build
npm run preview
git push main或 workflow_dispatch
CI / HOST · 构建与托管.github/workflows/deploy.yml
  1. Checkoutactions/checkout@v6
  2. Buildwithastro/action@v6,package manager 为 npm。
  3. Deployactions/deploy-pages@v5 等待 build job。
  4. HostPages 返回静态站点。
需运行验证Pages Source、最新 workflow 结果与线上页面是否更新
BOUNDARYnpm run dev 成功不等于生产构建成功;配置存在也不等于最新部署已经完成。

出问题时按现象定位代码,不要从根目录盲找

R06 · CODE LOCATOR先问“哪里表现错了”,再沿最短链路找到责任文件

下面不是完整目录说明,而是从现象到入口、继续追踪点和验证动作的最短排查图。

现象 01 · 某个 URL 不对先看 src/pages/

按访问路径找到同名页面;参数路由继续看 getStaticPaths()

验证:build 是否生成对应路径
现象 02 · 文章没出现先看 frontmatter 与公开过滤

检查 schema、draftprivate、分类、系列与页面过滤条件。

验证:collection 读取与列表结果
现象 03 · 正文或目录异常先看详情页与目标 MDX

确认 render(post)<Content />、H2/H3 headings 和组件 import。

验证:单篇编译与实际页面
现象 04 · 全站视觉异常先看 Layout 与 global.css

导航、主题和文章外壳属于全局;单篇信息图只排查文章专属 class。

验证:浅色、深色与 390px 页面
现象 05 · 本地能看、build 失败先看构建输出的首个错误

沿文件和行号检查 MDX 语法、导入路径、schema 与构建期代码。

验证:npm test + npm run build
现象 06 · build 通过、线上没变再看 Actions 与 Pages

确认 main 推送、build/deploy jobs、Pages Source 和实际访问结果。

状态:需运行验证
RUNTIME TAKEAWAYURL 找 page,文章找 collection,展示找 render / component / layout,产物找 build,线上找 workflow 与 Pages。

需要理解这些文件为何这样分区,再回到系列上一篇的系统蓝图;需要修代码,则继续沿本篇的最短执行链定位。