内容优先的技术选择
博客的大部分页面在发布后不会频繁变化。Astro 默认输出静态 HTML,只在搜索、主题切换等必要区域加载脚本,非常适合这类内容型网站。
选择框架时我关注的不是“能不能做出页面”,而是内容增长后的维护成本:文章元数据能否被校验,路由能否自动生成,RSS 与站点地图是否容易接入,以及页面是否在没有 JavaScript 时仍然可读。
Astro 的组件在构建阶段执行,默认不会把组件运行时发送到浏览器。对博客来说,这意味着导航、文章列表、标签页和正文可以直接输出 HTML;只有真正需要状态的搜索框、主题切换等功能才加载少量脚本。
内容集合
内容集合通过 Schema 约束文章元数据。在编写 Markdown 时,就能提前发现日期格式、标签类型或必填字段的问题。下面是本站使用的简化版本:
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
description: z.string().default(''),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
tags: z.array(z.string()).default([]),
categories: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
这样做的价值不只是类型提示。构建失败会直接指出哪篇文章缺少标题、日期写错或标签不是数组,问题不会等到部署后才暴露。
从内容生成路由
列表页先读取集合、过滤草稿并按日期排序;详情页通过 getStaticPaths 为每篇文章生成路径。分类、标签、归档和 RSS 都应该复用同一份内容数据,而不是分别维护数组。
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
export async function getStaticPaths() {
const posts = await getCollection('blog', ({ data }) => !data.draft);
return posts.map((post) => ({ params: { slug: post.id }, props: { post } }));
}
路由来自文件名,标题则来自 frontmatter。文件名一旦发布就尽量不要修改;如果必须调整,应配置重定向,避免外部链接和搜索引擎索引失效。
保持页面轻量
每个交互组件都应该回答一个问题:它是否真的需要在浏览器中运行?能用 HTML 和 CSS 完成的部分,不必交给客户端框架。
本站采用的策略是:正文、分页和导航完全静态;主题切换用一小段原生脚本;全文搜索在构建结束后由 Pagefind 扫描生成索引。这样搜索能力不会要求站点运行数据库或服务器函数。
{
"scripts": {
"build": "astro build && pagefind --site dist"
}
}
如果使用 React/Vue/Svelte 组件,Astro 也不会默认水合。只有加上 client:load、client:idle 或 client:visible 等指令,组件代码才会进入浏览器。应选择最晚且满足体验的加载时机。
内容站不能漏掉的基础设施
- 为每篇文章生成唯一的标题、描述和 canonical URL。
- 提供 RSS 与 sitemap,让订阅器和搜索引擎稳定发现内容。
- 图片声明宽高并使用现代格式,避免正文加载时跳动。
- 代码块保证横向滚动,不让长命令撑破移动端布局。
- 归档页、标签页与 404 页面在纯静态部署下也要可用。
一套可持续的写作流程
我的发布流程是:新建 Markdown、填写 frontmatter、本地预览、运行构建、检查死链与移动端布局,最后提交。日期与标签由 Schema 校验,构建产物则验证所有静态路由和搜索索引。
内容站的性能通常不是靠复杂优化得到的,而是从架构上避免不必要的客户端运行时。让 HTML 承担内容,让少量 JavaScript 只承担交互,长期维护会轻松很多。