名称:nuxt-content 描述:Nuxt Content v3 是基于 Git 的内容管理系统,用于 Markdown/MDC 内容站点。适用于博客、文档、内容驱动应用,支持类型安全查询、模式验证(Zod/Valibot)、全文本搜索、导航工具。兼容 Nuxt Studio 生产编辑、Cloudflare D1/Pages 部署、Vercel 部署、SQL 存储、MDC 组件、内容集合。
关键词:nuxt content,@nuxt/content,内容集合,基于 git 的 cms,markdown cms,mdc 语法,nuxt studio,内容编辑,queryCollection,cloudflare d1 部署,vercel 部署,markdown 组件,prose 组件,内容导航,全文本搜索,类型安全查询,sql 存储,内容模式验证,zod 验证,valibot,远程仓库,内容查询,queryCollectionNavigation,queryCollectionSearchSections,ContentRenderer 组件 许可证:MIT
Nuxt Content v3
状态:生产就绪 最后更新:2025-01-10 依赖:无 最新版本:@nuxt/content@^3.0.0,nuxt-studio@^0.1.0-alpha,zod@^4.1.12,valibot@^0.42.0,better-sqlite3@^11.0.0
概述
Nuxt Content v3 是一个强大的基于 Git 的内容管理系统,用于 Nuxt 项目,通过 Markdown、YAML、JSON 和 CSV 文件管理内容。它将内容文件转换为结构化数据,提供类型安全查询、自动验证和基于 SQL 的存储以优化性能。
v3 的新特性
主要改进:
- 内容集合:结构化数据组织,支持类型安全查询、自动验证和高级查询构建器
- 基于 SQL 的存储:生产环境使用 SQL(相比 v2 的大包大小)以优化查询和通用兼容性(服务器/无服务器/边缘/静态)
- 完全 TypeScript 集成:自动为所有集合和 API 生成类型
- 增强性能:通过适配器基 SQL 系统实现超快速数据检索
- Nuxt Studio 集成:生产环境中自托管的 GitHub 同步内容编辑
何时使用此技能
使用此技能当:
- 构建博客、文档站点或内容密集型应用
- 使用 Markdown、YAML、JSON 或 CSV 文件管理内容
- 实施基于 Git 的内容工作流
- 创建类型安全的内容查询
- 部署到 Cloudflare(Pages/Workers)或 Vercel
- 使用 Nuxt Studio 设置生产内容编辑
- 构建可搜索内容与全文本搜索
- 从内容结构创建导航系统
快速开始(10分钟)
1. 安装 Nuxt Content
# Bun(推荐)
bun add @nuxt/content better-sqlite3
# npm
npm install @nuxt/content better-sqlite3
# pnpm
pnpm add @nuxt/content better-sqlite3
为何重要:
@nuxt/content是核心 CMS 模块better-sqlite3提供 SQL 存储以优化性能- 基本使用无需配置
2. 注册模块
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content']
})
关键:
- 模块必须添加到
modules数组(非buildModules) - 基本设置无需额外配置
3. 创建第一个集合
// content.config.ts
import { defineContentConfig, defineCollection } from '@nuxt/content'
import { z } from 'zod'
export default defineContentConfig({
collections: {
content: defineCollection({
type: 'page',
source: '**/*.md',
schema: z.object({
tags: z.array(z.string()).optional(),
date: z.date().optional()
})
})
}
})
创建内容文件:
<!-- content/index.md -->
---
标题:Hello World
描述:我的第一个 Nuxt Content 页面
标签:['nuxt', 'content']
---
# 欢迎使用 Nuxt Content v3
这是我的第一个内容驱动站点!
4. 查询和渲染内容
<!-- pages/[...slug].vue -->
<script setup>
const route = useRoute()
const { data: page } = await useAsyncData(route.path, () =>
queryCollection('content').path(route.path).first()
)
</script>
<template>
<ContentRenderer v-if="page" :value="page" />
</template>
查看完整模板:templates/blog-collection-setup.ts
关键规则
始终要做
- 定义集合在
content.config.ts中,然后再查询 - 使用 ISO 8601 日期格式:
2024-01-15或2024-01-15T10:30:00Z - 重启开发服务器在更改
content.config.ts后 - 使用
.only()选择特定字段(性能优化) - 放置 MDC 组件在
components/content/目录中 - 使用零填充前缀用于数字排序:
01-、02- - 安装数据库连接器:
better-sqlite3必需 - 指定语言在代码块中以启用语法高亮
- 使用
<!--more-->在内容中分隔摘要 - D1 绑定必须为 “DB”(区分大小写)在 Cloudflare 上
永远不要做
- 不要在
content.config.ts中定义集合前查询 - 不要使用非 ISO 日期格式(例如,“January 15, 2024”)
- 不要忘记重启开发服务器在配置更改后
- 不要查询所有字段当只需要部分时(使用
.only()) - 不要放置组件在
components/content/目录外 - 不要使用单数字前缀(使用
01-而非1-) - 不要跳过数据库连接器安装
- 不要忘记语言在代码围栏中
- 不要期望摘要没有
<!--more-->分隔符 - 不要使用不同绑定名称对于 D1(必须为 “DB”)
内容集合
定义集合
集合组织相关内容与共享配置:
// content.config.ts
import { defineCollection, defineContentConfig } from '@nuxt/content'
import { z } from 'zod'
export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/**/*.md',
schema: z.object({
title: z.string(),
date: z.date(),
tags: z.array(z.string()).default([])
})
}),
authors: defineCollection({
type: 'data',
source: 'authors/*.yml',
schema: z.object({
name: z.string(),
bio: z.string()
})
})
}
})
集合类型
页面类型(type: 'page')
用于:映射到 URL 的内容
特性:
- 从文件结构自动生成路径
- 内置字段:
path、title、description、body、navigation - 完美用于:博客、文档、营销页面
路径映射:
content/index.md → /
content/about.md → /about
content/blog/hello.md → /blog/hello
数据类型(type: 'data')
用于:无 URL 的结构化数据
特性:
- 完整模式控制
- 无自动路径生成
- 完美用于:作者、产品、配置
模式验证
使用 Zod v4(推荐)
bun add -D zod@^4.1.12
# 或:npm install -D zod@^4.1.12
import { z } from 'zod'
schema: z.object({
title: z.string(),
date: z.date(),
published: z.boolean().default(false),
tags: z.array(z.string()).optional(),
category: z.enum(['news', 'tutorial', 'update'])
})
使用 Valibot(替代)
bun add -D valibot@^0.42.0
# 或:npm install -D valibot@^0.42.0
import * as v from 'valibot'
schema: v.object({
title: v.string(),
date: v.date(),
published: v.boolean(false),
tags: v.optional(v.array(v.string()))
})
查询内容
基本查询
// 获取所有文章
const posts = await queryCollection('blog').all()
// 通过路径获取单篇文章
const post = await queryCollection('blog').path('/blog/hello').first()
// 通过 ID 获取文章
const post = await queryCollection('blog').where('_id', '=', 'hello').first()
字段选择
// 选择特定字段(性能优化)
const posts = await queryCollection('blog')
.only(['title', 'description', 'date', 'path'])
.all()
// 排除字段
const posts = await queryCollection('blog')
.without(['body'])
.all()
条件查询
// 单条件
const posts = await queryCollection('blog')
.where('published', '=', true)
.all()
// 多条件
const posts = await queryCollection('blog')
.where('published', '=', true)
.where('category', '=', 'tutorial')
.all()
// 操作符:=, !=, <, <=, >, >=, in, not-in, like
const recent = await queryCollection('blog')
.where('date', '>', new Date('2024-01-01'))
.all()
const tagged = await queryCollection('blog')
.where('tags', 'in', ['nuxt', 'vue'])
.all()
排序和分页
// 按字段排序
const posts = await queryCollection('blog')
.sort('date', 'DESC')
.all()
// 分页
const posts = await queryCollection('blog')
.sort('date', 'DESC')
.limit(10)
.offset(0)
.all()
计数
// 计数结果
const total = await queryCollection('blog')
.where('published', '=', true)
.count()
服务器端查询
// server/api/posts.get.ts
export default defineEventHandler(async (event) => {
const posts = await queryCollection('blog')
.where('published', '=', true)
.sort('date', 'DESC')
.all()
return { posts }
})
导航
从内容结构自动生成导航树:
const navigation = await queryCollectionNavigation('blog').all()
返回层级结构:
[
{
title: '入门指南',
path: '/docs/getting-started',
children: [{ title: '安装', path: '/docs/getting-started/installation' }]
}
]
高级模式(过滤、排序、自定义结构):参见 references/collection-examples.md
MDC 语法(Markdown 组件)
基本组件语法
<!-- 默认插槽 -->
::my-alert
这是一个警告消息
::
<!-- 命名插槽 -->
::my-card
#title
卡片标题
#default
卡片内容在此
::
组件:
<!-- components/content/MyAlert.vue -->
<template>
<div class="alert">
<slot />
</div>
</template>
属性
::my-button{href="/docs" type="primary"}
点击我
::
组件:
<!-- components/content/MyButton.vue -->
<script setup>
defineProps<{
href: string
type: 'primary' | 'secondary'
}>()
</script>
全文本搜索
// 搜索所有内容
const results = await queryCollectionSearchSections('blog', 'nuxt content')
.where('published', '=', true)
.all()
返回:
[
{
id: 'hello',
path: '/blog/hello',
title: 'Hello World',
content: '...匹配文本...'
}
]
部署
Cloudflare Pages + D1
bun add -D @nuxthub/core
bunx wrangler d1 create nuxt-content
bun run build && bunx wrangler pages deploy dist
wrangler.toml:
[[d1_databases]]
binding = "DB" # 必须精确为 "DB"(区分大小写)
database_name = "nuxt-content"
database_id = "你的数据库id"
关键:D1 绑定名称必须为 DB(区分大小写)。
参见:references/deployment-checklists.md 获取完整的 Cloudflare 部署指南与故障排除。
Vercel
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content'],
routeRules: {
'/blog/**': { prerender: true }
}
})
vercel deploy
参见:references/deployment-checklists.md 获取完整的 Vercel 配置和预渲染策略。
Nuxt Studio 集成
启用 Studio
bun add -D nuxt-studio@alpha
# 或:npm install -D nuxt-studio@alpha
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content', 'nuxt-studio'],
studio: {
enabled: true,
gitInfo: {
name: '你的名字',
email: '你的邮箱@example.com'
}
}
})
OAuth 设置
- 创建 GitHub OAuth 应用
- 设置授权回调 URL:
https://yourdomain.com/api/__studio/oauth/callback - 添加客户端 ID 和密钥到环境变量
关键:回调 URL 必须完全匹配生产域名(包括 https://)。
前5个关键问题
问题 #1:集合未找到
错误:集合 'xyz' 未找到
解决方案:在 content.config.ts 中定义集合并重启开发服务器
rm -rf .nuxt && bun dev
问题 #2:日期验证失败
错误:验证错误:期望日期,接收到字符串
解决方案:使用 ISO 8601 格式:
---
date: 2024-01-15 # ✅ 正确
# 非:"January 15, 2024" # ❌ 错误
---
问题 #3:D1 绑定未找到(Cloudflare)
错误:DB 未定义
解决方案:D1 绑定名称在 Cloudflare 仪表板中必须精确为 DB(区分大小写)。
问题 #4:MDC 组件未渲染
错误:组件显示为原始文本
解决方案:将组件放置在 components/content/ 目录中,名称精确匹配:
<!-- components/content/MyAlert.vue -->
::my-alert
内容
::
问题 #5:导航未更新
错误:新内容未出现在导航中
解决方案:清除 .nuxt 缓存:
rm -rf .nuxt && bun dev
参见所有18个问题:references/error-catalog.md
何时加载参考文档
加载 references/error-catalog.md 当:
- 用户遇到超出前5个问题所示的错误
- 调试集合验证、模式错误或部署问题
- 需要完整的错误目录,包含所有18个文档化解决方案和来源
加载 references/collection-examples.md 当:
- 设置高级集合类型(数据 vs 页面)
- 实施复杂模式验证模式
- 需要 Zod v4 或 Valibot 模式示例
- 处理多个集合配置
加载 references/query-operators.md 当:
- 构建超越基本示例的复杂查询
- 需要所有操作符的完整参考(=, !=, <, >, <=, >=, in, not-in, like)
- 实施分页、排序或字段选择
- 故障排除查询语法错误
加载 references/deployment-checklists.md 当:
- 部署到特定平台(Cloudflare Pages、Cloudflare Workers D1、Vercel)
- 设置生产环境配置
- 故障排除部署特定错误(D1 绑定、预渲染路由)
- 需要平台特定的 wrangler.toml 或 vercel.json 示例
加载 references/mdc-syntax-reference.md 当:
- 实施自定义 MDC(Markdown 组件)
- 调试组件渲染问题
- 需要完整的属性、插槽、嵌套语法参考
- 创建高级内容组件
加载 references/studio-setup-guide.md 当:
- 为生产内容编辑设置 Nuxt Studio
- 配置 GitHub OAuth 认证
- 启用自托管内容编辑与 GitHub 同步
- 故障排除 Studio 认证或 Git 同步问题
模板(templates/):
blog-collection-setup.ts- 完整的博客设置,包括集合、查询、导航、搜索和部署(334行)
性能提示
- 使用
.only()选择特定字段 - 启用缓存用于生产
- 使用分页用于大型集合
- 预渲染静态路由在 Vercel 上
- 使用 SQL 存储(better-sqlite3)以优化性能
最佳实践
- 始终先定义集合并再查询
- 使用 TypeScript 进行类型安全查询
- 使用 Zod 或 Valibot 验证模式
- 将 MDC 组件放置在
components/content/目录中 - 使用 ISO 8601 日期格式
- 添加
<!--more-->用于摘要 - 在代码块中指定语言
- 配置更改后清除
.nuxt缓存 - 对于 Cloudflare:D1 绑定必须为 “DB”
- 使用分页用于大型数据集
与其他技能的集成
此技能可与以下技能良好组合:
- nuxt-v4 → 核心 Nuxt 框架
- nuxt-ui-v4 → UI 组件库
- cloudflare-worker-base → Cloudflare 部署
- tailwind-v4-shadcn → 样式
- drizzle-orm-d1 → 额外数据库查询
- cloudflare-d1 → 数据库集成
额外资源
官方文档:
- Nuxt Content 文档:https://content.nuxt.com
- GitHub:https://github.com/nuxt/content
- Nuxt Studio:https://nuxt.studio
示例:
生产测试:文档站点、博客、内容平台 最后更新:2025-01-27 令牌节省:约60%(减少内容 + 错误文档)