首页
/ Astro Blog Starter Kit 深度解析:从零搭建支持 Sitemap、RSS 与 MDX 的内容驱动博客

Astro Blog Starter Kit 深度解析:从零搭建支持 Sitemap、RSS 与 MDX 的内容驱动博客

2026-09-04 14:22:27作者:沈韬淼Beryl

本篇技术指南以 Astro 官方仓库中的 Blog Starter Kit(examples/blog)为主体,完整讲解该模板的项目结构、集成配置、内容集合定义与构建命令,并结合模板内真实源码(内容 Schema、RSS 端点、SEO 头部组件等)剖析其实现原理。读完后你将掌握如何用一条命令初始化博客、如何用 Zod Schema 对 Markdown 前置元数据做类型检查,以及如何复用模板的 Sitemap、RSS 与 Open Graph 方案搭建生产可用的内容站点。

1. 模板定位与核心特性

Astro 官方在 examples/blog/README.md 中将 Blog Starter Kit 定位为"内容驱动网站"的轻量起点,适合个人网站、博客和作品集。模板自带的核心特性如下(摘自原文档的 Features 清单):

  • 极简样式(Minimal styling),可自由改造;
  • 100/100 Lighthouse 性能表现;
  • SEO 友好:输出 canonical URL 与 Open Graph 数据;
  • 内置 Sitemap 支持;
  • 内置 RSS Feed 支持;
  • 同时支持 Markdown 与 MDX 内容格式。

examples/blog/package.json 可以看到,这些特性对应四组运行时依赖:astro(^7.2.10)、@astrojs/mdx(MDX 支持)、@astrojs/rss(RSS 生成)、@astrojs/sitemap(Sitemap 生成),另外还依赖 sharp 用于图片处理。该模板要求 Node.js >=22.12.0(见 package.json 的 engines 字段),初始化前需确认本机 Node 版本满足该前提。

2. 初始化命令与目录结构

2.1 一条命令创建项目

npm create astro@latest -- --template blog

该命令从 Astro 模板库中拉取 blog 模板并交互式完成初始化。仓库内 examples/blog 目录本身就是该模板的完整参照实现,可直接在仓库中查阅、复制其源码结构。

2.2 项目结构

原文档给出的目录结构如下,本文在此基础上结合仓库实际文件逐一说明职责:

├── public/
├── src/
│   ├── assets/
│   ├── components/
│   ├── content/
│   ├── layouts/
│   └── pages/
├── astro.config.mjs
├── README.md
├── package.json
└── tsconfig.json

结合仓库实际内容,各目录的具体职责为:

  • src/pages/:路由目录。Astro 在此目录中查找 .astro.md 文件,每个文件按其文件名暴露为一个路由。模板实际包含 index.astro(首页)、about.astro(关于页)、blog/index.astro(文章列表页)、blog/[...slug].astro(动态文章详情页)以及 rss.xml.js(RSS 端点);
  • src/components/:存放 Astro 组件。模板包含 BaseHead.astro(全局 head)、Header.astro/Footer.astro(页眉页脚)、HeaderLink.astroFormattedDate.astro(日期格式化);
  • src/content/:存放"集合(collections)"相关的 Markdown 与 MDX 文档,src/content/blog/ 下有 4 篇 Markdown 文章(如 first-post.md)和 1 篇 MDX 文章(using-mdx.mdx);
  • src/layouts/:页面布局组件,模板提供 BlogPost.astro 作为文章详情页布局;
  • src/assets/:静态资源,模板预置了 6 张 blog-placeholder-*.jpg 占位图与两个 Atkinson 字体的 woff 文件(src/assets/fonts/);
  • public/:任意静态资源(如 favicon.icofavicon.svg),构建时按原路径直接拷贝。

原文档还说明:src/components/ 没有特殊含义,只是团队习惯把 Astro/React/Vue/Svelte/Preact 等框架组件统一放在这里;全局站点数据则集中在 src/consts.ts,通过 import 在任何位置复用:

export const SITE_TITLE = 'Astro Blog';
export const SITE_DESCRIPTION = 'Welcome to my website!';

3. astro.config.mjs:站点、集成与字体配置

examples/blog/astro.config.mjs 是模板的关键配置入口,完整内容如下:

import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
import { defineConfig, fontProviders } from 'astro/config';

export default defineConfig({
	site: 'https://example.com',
	integrations: [mdx(), sitemap()],
	fonts: [
		{
			provider: fontProviders.local(),
			name: 'Atkinson',
			cssVariable: '--font-atkinson',
			fallbacks: ['sans-serif'],
			options: {
				variants: [
					{
						src: ['./src/assets/fonts/atkinson-regular.woff'],
						weight: 400,
						style: 'normal',
						display: 'swap',
					},
					{
						src: ['./src/assets/fonts/atkinson-bold.woff'],
						weight: 700,
						style: 'normal',
						display: 'swap',
					},
				],
			},
		},
	],
});

各配置项的作用与源码印证:

  1. site:声明站点根 URL。它不仅是 SEO 的地基——canonical URL、Open Graph 图片等绝对地址都基于它拼接(见下文 BaseHead 分析)——还是 sitemap()rss() 集成生成完整链接的前提,部署前必须替换为真实域名;
  2. integrations: [mdx(), sitemap()]:注册 MDX 与 Sitemap 集成。@astrojs/mdx 使 src/content/blog/ 中的 .mdx 文件(可嵌入 React 等客户端组件)被 Astro 解析;@astrojs/sitemap 在构建时自动生成 sitemap-index.xml,并在每个页面的 head 中注入对应引用;
  3. fonts:使用 Astro 内置字体管理,fontProviders.local() 声明使用本地字体文件,cssVariable: '--font-atkinson' 将字体注册为 CSS 变量,display: 'swap' 控制字体加载策略(先显示回退字体 sans-serif,字体就绪后切换)。页面中通过 BaseHead.astro 里的 <Font cssVariable="--font-atkinson" preload /> 组件完成预加载。

4. 内容集合:glob 加载器与 Zod Schema 类型检查

原文档指出 src/content/ 目录存放"集合"相关的 Markdown/MDX 文档,并用 getCollection()src/content/blog/ 获取文章、用可选 Schema 对前置元数据做类型检查。模板的 Schema 定义在 src/content.config.ts,完整实现如下:

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

const blog = defineCollection({
	// 加载 src/content/blog/ 目录下的 Markdown 和 MDX 文件。
	loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
	// 使用 Schema 对前置元数据做类型检查
	schema: ({ image }) =>
		z.object({
			title: z.string(),
			description: z.string(),
			// 将字符串转换为 Date 对象
			pubDate: z.coerce.date(),
			updatedDate: z.coerce.date().optional(),
			heroImage: z.optional(image()),
		}),
});

export const collections = { blog };

几个值得注意的实现细节:

  • glob 加载器base: './src/content/blog' 指定内容目录,pattern: '**/*.{md,mdx}' 同时匹配 Markdown 与 MDX,这正是"Markdown & MDX 支持"特性的落地方式;
  • z.coerce.date():把前置元数据中的字符串日期(如 'Jul 08 2022')强制转换为 Date 对象,保证后续排序可以直接调用 pubDate.valueOf()
  • image() 字段验证器heroImage 字段使用 Schema 上下文注入的 image 验证器校验图片资源,构建期即可发现图片路径错误;
  • 前置元数据示例first-post.md 的 frontmatter 严格对应上述 Schema:
---
title: 'First post'
description: 'Lorem ipsum dolor sit amet'
pubDate: 'Jul 08 2022'
heroImage: '../../assets/blog-placeholder-3.jpg'
---

由于 Schema 已声明,TypeScript 中对 CollectionEntry<'blog'> 的访问可获得完整类型提示——src/layouts/BlogPost.astro 正是用 type Props = CollectionEntry<'blog'>['data'] 精确约束了布局组件的入参。

5. 路由实现:从文章列表到动态详情页

5.1 文章列表页(/blog/

examples/blog/src/pages/blog/index.astro 的 frontmatter 展示了典型的"取数据—排序—渲染"三步:

import { getCollection } from 'astro:content';

const posts = (await getCollection('blog')).sort(
	(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
);

getCollection('blog') 返回 blog 集合的全部条目,sort 依赖 Schema 中 z.coerce.date() 转换后的 Date 对象按发布时间倒序排列。渲染时每条文章生成指向 /blog/${post.id}/ 的链接,并配合 <Image width={720} height={360} src={post.data.heroImage} /> 输出尺寸明确的缩略图(post.id 由内容加载器基于文件路径生成)。

5.2 动态详情页(/blog/[...slug]/

examples/blog/src/pages/blog/[...slug].astro 是整份模板中最核心的页面之一,它展示了 Astro 内容驱动渲染的完整链路,全文仅 20 行:

---
import { type CollectionEntry, getCollection, render } from 'astro:content';
import BlogPost from '../../layouts/BlogPost.astro';

export async function getStaticPaths() {
	const posts = await getCollection('blog');
	return posts.map((post) => ({
		params: { slug: post.id },
		props: post,
	}));
}
type Props = CollectionEntry<'blog'>;

const post = Astro.props;
const { Content } = await render(post);
---

<BlogPost {...post.data}>
	<Content />
</BlogPost>

从源码实现看,其工作机制分四层:

  1. getStaticPaths():构建期为集合中每篇文章生成一条静态路径,slug 参数取文章 id(即内容文件路径),这是 SSG 预渲染"每篇文章一个 URL"的关键;
  2. CollectionEntry<'blog'>:以类型系统约束 Astro.props,保证 post.data 的每个字段(title、pubDate、heroImage 等)都有类型;
  3. render(post):把 Markdown/MDX 源文件渲染为可复用的 <Content /> 组件,而不是 HTML 字符串,因此内容可以插入任意布局位置;
  4. 布局复用:渲染结果作为 <BlogPost> 的默认插槽传入,frontmatter 数据通过 {...post.data} 展开为布局 props。

BlogPost.astro 则负责文章页的"壳":hero 图(存在时以 width={1020} height={510} 渲染)、标题、发布日期(<FormattedDate /> 组件)、可选的 updatedDate 提示行,以及正文 <slot />

6. SEO 与站点级输出:canonical、Open Graph 与 Sitemap

模板宣称"SEO 友好",其实现集中在 src/components/BaseHead.astro,所有页面(首页、列表页、文章页布局)都通过引入该组件获得一致的 head 输出。关键实现:

---
import '../styles/global.css';
import { SITE_TITLE } from '../consts';
import { Font } from 'astro:assets';

const canonicalURL = new URL(Astro.url.pathname, Astro.site);
---

<link rel="canonical" href={canonicalURL} />
<link rel="sitemap" href="/sitemap-index.xml" />
<link
	rel="alternate"
	type="application/rss+xml"
	title={SITE_TITLE}
	href={new URL('rss.xml', Astro.site)}
/>
<meta name="generator" content={Astro.generator} />
<Font cssVariable="--font-atkinson" preload />

<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={new URL(image.src, Astro.url)} />
<meta name="twitter:card" content="summary_large_image" />

要点解析:

  • canonical URLnew URL(Astro.url.pathname, Astro.site) 把当前路径与 site 配置拼接为绝对地址,搜索引擎据此识别规范页面;
  • Open Graph / Twitter Cardog:urlog:titleog:descriptionog:imagetwitter:card 完整覆盖社交分享卡片所需的元数据,og:image 未提供时回退到 blog-placeholder-1.jpg
  • Sitemap 与 RSS 的互相引用:head 中同时声明 /sitemap-index.xmlrss.xml 的绝对 URL,使站点输出自描述——这正是 README 特性清单中"Sitemap support"与"RSS Feed support"两条的落地位置。

7. RSS Feed:用 Endpoint 端点生成 /rss.xml

RSS 由 examples/blog/src/pages/rss.xml.js 实现,这是一个 Astro Endpoint(非 HTML 响应页面),完整代码:

import { getCollection } from 'astro:content';
import rss from '@astrojs/rss';
import { SITE_DESCRIPTION, SITE_TITLE } from '../consts';

export async function GET(context) {
	const posts = await getCollection('blog');
	return rss({
		title: SITE_TITLE,
		description: SITE_DESCRIPTION,
		site: context.site,
		items: posts.map((post) => ({
			...post.data,
			link: `/blog/${post.id}/`,
		})),
	});
}

实现逻辑:GET 导出函数使 rss.xml.js 在构建时被预渲染为 /rss.xml@astrojs/rssrss() 辅助函数接收站点标题、描述、context.site 根地址以及由集合条目映射出的 items(每项携带 frontmatter 全部字段与文章链接)。由于条目直接来自 getCollection('blog'),新增文章无需任何额外操作即自动进入 Feed——RSS、Sitemap 与文章路由三者共享同一数据源,保证了内容输出的一致性。

8. 常用命令速查

原文档给出的命令表全部从项目根目录的终端执行,完整保留如下:

Command Action
npm install 安装依赖
npm run dev localhost:4321 启动本地开发服务器
npm run build 构建生产站点到 ./dist/
npm run preview 部署前本地预览构建产物
npm run astro ... 运行 astro addastro check 等 CLI 命令
npm run astro -- --help 查看 Astro CLI 帮助

这些命令与 examples/blog/package.jsonscripts 字段一一对应(devastro devbuildastro buildpreviewastro previewastro → 直接透传 astro CLI)。构建产出的静态文件位于 dist/,可直接部署到任意静态托管。

9. 上手路线:修改模板的五个入口

模板首页 src/pages/index.astro 本身也是一份"操作指南",原文档推荐的定制路线与之对应:

  1. 编辑 src/pages/index.astro 修改首页文案与结构;
  2. 编辑 src/components/Header.astro 调整页眉导航项;
  3. src/components/Footer.astro 中加入署名信息;
  4. src/content/blog/ 下新增 .md.mdx 文件写文章(frontmatter 必须满足第 4 节 Schema:titledescriptionpubDate 必填,updatedDateheroImage 可选);
  5. 定制 src/layouts/BlogPost.astro 改变文章详情页的排版与 hero 图尺寸。

此外,astro.config.mjs 中的 site 是上线前必改项:它同时驱动 canonical URL、Open Graph 绝对地址、Sitemap 与 RSS 链接,留作 https://example.com 会导致这四类输出全部指向错误域名。

10. 小结

Astro Blog Starter Kit 的完整价值在于"内容—路由—SEO 输出"三条链路的闭环示范:glob 加载器加 Zod Schema 在构建期完成内容校验与类型推导,getCollection() + getStaticPaths() + render() 完成 SSG 渲染,BaseHead + sitemap() + rss.xml Endpoint 保证每个页面与站点级文件输出一致的元数据。对需要在 Astro 上落地博客的开发者而言,examples/blog 目录即是可直接参照的完整实现,文中引用的每个文件(examples/blog/ 下的配置、内容集合、页面与组件)都可以在当前仓库中直接查阅验证。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384