博客
学习如何创建、管理和自定义博客文章
TanStarter 内置一个基于 Content Collections 构建的博客系统。
博客系统架构
博客系统使用 Content Collections 构建,在项目根目录的 content-collections.ts 中定义集合配置。
hello-world.md
getting-started.md
deploy-to-production.md
index.tsx
$slug.tsx
blog-card.tsx
blog-grid.tsx
blog-pagination.tsx
blog.ts
content-collections.ts
数据源配置
博客系统在 content-collections.ts 文件中使用 defineCollection 定义数据集合:
import { defineCollection, defineConfig } from '@content-collections/core';
import { z } from 'zod';
const blog = defineCollection({
name: 'blog',
directory: 'content/blog',
include: '**/*.md',
schema: z.object({
title: z.string(),
description: z.string(),
date: z.string(),
category: z.string(),
content: z.string(),
image: z.url(),
}),
transform: (doc) => ({
...doc,
slug: doc._meta.path,
}),
});
export default defineConfig({
collections: [blog],
});然后在 src/lib/blog.ts 中使用 content-collections 提供的数据进行查询:
import { allBlogs } from 'content-collections';
import type { Blog } from 'content-collections';
export type BlogPost = Blog & { slug: string };
export function getSortedPosts(): BlogPost[] {
return [...(allBlogs as BlogPost[])].sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
}
export function getPostBySlug(slug: string): BlogPost | undefined {
return (allBlogs as BlogPost[]).find((p) => p.slug === slug);
}
export function getPaginatedPosts(page: number) {
const sorted = getSortedPosts();
// ... pagination logic
}创建博客内容
添加新博客文章
在 content/blog 目录中创建新的 .md 文件:
---
title: My First Blog Post
description: This is a brief description of my first blog post.
date: 2026-01-15
category: Tutorial
image: https://example.com/images/blog/my-first-post.jpg
---
# Introduction
This is my first blog post. You can use **Markdown** here.
## Section 1
Some content here...
## Section 2
More content here...Frontmatter 字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
title | string | ✅ | 文章标题 |
description | string | ✅ | 文章摘要描述 |
date | string | ✅ | 发布日期(格式:YYYY-MM-DD) |
category | string | ✅ | 文章分类(单个分类) |
image | URL | ✅ | 封面图片 URL |
TanStarter 的博客使用 .md Markdown 文件格式,每篇文章只有一个 category(字符串),不是多分类数组。
路由和页面
博客使用 TanStack Router 文件路由系统:
src/routes/blog/index.tsx:博客列表页,支持分页src/routes/blog/$slug.tsx:博客文章详情页
import { createFileRoute } from '@tanstack/react-router';
import { getPaginatedPosts } from '@/lib/blog';
export const Route = createFileRoute('/blog/')({
loader: ({ location }) => {
const page = Number(new URLSearchParams(location.search).get('page')) || 1;
return getPaginatedPosts(page);
},
component: BlogListPage,
});import { createFileRoute, notFound } from '@tanstack/react-router';
import { getPostBySlug } from '@/lib/blog';
export const Route = createFileRoute('/blog/$slug')({
loader: async ({ params }) => {
const post = getPostBySlug(params.slug);
if (!post) throw notFound();
return post;
},
component: BlogPostPage,
});自定义
配置分页
在 src/config/website.ts 中配置每页显示的文章数量:
export const websiteConfig: WebsiteConfig = {
// ...other config
blog: {
enable: true,
paginationSize: 6,
},
// ...other config
}更改博客文章卡片布局
在 src/components/blog/blog-card.tsx 中自定义博客卡片组件:
import type { BlogPost } from '@/lib/blog';
interface BlogCardProps {
post: BlogPost;
}
export function BlogCard({ post }: BlogCardProps) {
return (
<div className="group flex flex-col border rounded-lg overflow-hidden h-full bg-card shadow-sm hover:shadow-md transition-shadow">
<h3>{post.title}</h3>
<p>{post.description}</p>
<span>{post.category}</span>
<time>{post.date}</time>
{/* ... rest of the component */}
</div>
);
}自定义博客文章数据结构
要向博客文章添加新字段:
- 在
content-collections.ts中修改 schema - 更新组件以显示新字段
示例:添加"精选"字段
const blog = defineCollection({
name: 'blog',
directory: 'content/blog',
include: '**/*.md',
schema: z.object({
title: z.string(),
description: z.string(),
date: z.string(),
category: z.string(),
content: z.string(),
image: z.url(),
// Add new field
featured: z.boolean().default(false),
}),
transform: (doc) => ({
...doc,
slug: doc._meta.path,
}),
});然后,您可以在博客文章中使用此字段:
---
title: Important Announcement
description: Read this important announcement
date: 2026-01-15
category: Announcement
image: https://example.com/images/blog/announcement.jpg
featured: true
---
Content here...程序化查询文章
您可以使用 src/lib/blog.ts 中的工具函数查询文章:
import { getSortedPosts, getPostBySlug, getPaginatedPosts } from '@/lib/blog';
// Get all posts sorted by date
const allPosts = getSortedPosts();
// Get a specific post by slug
const post = getPostBySlug('hello-world');
// Get paginated posts
const { posts, totalPages, currentPage } = getPaginatedPosts(1);构建过程
博客系统使用 content-collections 的构建过程:
- 开发:在开发过程中 content-collections 会监视
content/目录的变化并自动重新生成 - 构建:Vite 构建时自动处理 content-collections,无需额外命令
- 生成的文件:
.content-collections目录包含生成的 TypeScript 文件
content-collections 与 Vite 集成,开发和构建过程完全自动化,无需手动运行额外命令。
最佳实践
- 使用高质量图片:为博客文章使用适当大小和优化的图片
- 一致的分类:在文章列表中保持一致的分类名称
- 清晰的元数据:编写清晰的标题和描述以提高 SEO 效果
- 结构化内容:在博客文章内容中使用适当的标题和段落
- 日期格式:使用
YYYY-MM-DD格式的日期字符串 - 使用 Zod 验证:使用 Zod 进行类型安全的内容验证
下一步
现在您了解了如何在 TanStarter 中使用博客系统,您可能想要探索这些相关功能:
TanStarter 文档