文章索引 
用 Nuxt 4 和 Content 把博客钉在静态文件上
这个站点没有后端服务,没有数据库,也没有管理后台。写作流程只有三步:新建一个 .md 文件,写完,提交。剩下的事情交给构建管道。
为什么坚持纯静态
动态博客系统的大部分能力我用不上。评论可以外挂,搜索可以在客户端做,统计交给日志。剩下的核心诉求只有一个:把文字变成网页。既然如此,服务器就是多余的运维负担。
静态文件带来的好处非常实在:
- 部署目标随便挑,对象存储、CDN、GitHub Pages 都行
- 没有可以被攻破的运行时,安全面趋近于零
- 首字节时间就是 CDN 的响应时间
- 整站可以塞进 Git,回滚等于
git revert
内容即文件
@nuxt/content 把 content/ 目录变成一个类型安全的集合。集合的形状在 content.config.ts 里声明:
ts
import { defineCollection, defineContentConfig, z } from '@nuxt/content'
export default defineContentConfig({
collections: {
posts: defineCollection({
type: 'page',
source: 'posts/*.md',
schema: z.object({
date: z.string(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
}),
},
})
frontmatter 写错字段,构建期就会报错,而不是等到页面渲染成 undefined 才发现。
页面里怎么查
查询 API 长得像一个极简的 ORM,在组件里直接用:
vue
<script setup lang="ts">
const { data: posts } = await useAsyncData('posts', () =>
queryCollection('posts')
.where('draft', '=', false)
.order('date', 'DESC')
.all(),
)
</script>
同一套 API 在 server route 里也能用,只是第一个参数换成 event。RSS 和站点地图就是这么生成的。
一条命令产出整站
bash
pnpm generate
nuxt generate 会启动一次性渲染,从 / 开始爬取站内链接,把每个路由渲染成 HTML,连同 RSS、站点地图一起写进 .output/public。那个目录就是最终产物,扔到任何静态托管上都能跑。
值得强调的是:预渲染的页面依然是完整的 Vue 应用。首屏是纯 HTML,水合之后站内跳转走客户端路由,无需再请求服务器。
写作体验
因为文章就是仓库里的文件,所有代码工具都顺便变成了写作工具:全文搜索用编辑器自带的,版本历史是 Git 的提交记录,写一半的文章加上 draft: true 就不会被发布。
这套组合不惊艳,但非常耐用。工具退到背景里,剩下的只有写字这件事本身。