添加博客功能
将你的 VitePress 站点转换为功能完善的博客。
功能规划
- 文章列表页
- 分类和标签
- 文章详情页
- 归档页面
- RSS 订阅
步骤 1:创建博客布局
博客列表布局
vue
<!-- .vitepress/theme/layouts/BlogLayout.vue -->
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useData, useRouter } from 'vitepress'
interface Post {
title: string
path: string
date: string
excerpt: string
tags: string[]
readingTime?: number
}
const { frontmatter } = useData()
const router = useRouter()
const posts = ref<Post[]>(frontmatter.value.posts || [])
const selectedTag = ref<string | null>(null)
const searchQuery = ref('')
const allTags = computed(() => {
const tags = new Set<string>()
posts.value.forEach(p => p.tags?.forEach(t => tags.add(t)))
return Array.from(tags)
})
const filteredPosts = computed(() => {
let result = posts.value
if (selectedTag.value) {
result = result.filter(p => p.tags?.includes(selectedTag.value!))
}
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
result = result.filter(p =>
p.title.toLowerCase().includes(query) ||
p.excerpt?.toLowerCase().includes(query)
)
}
return result
})
const navigate = (path: string) => {
router.go(path)
}
</script>
<template>
<div class="blog-layout">
<header class="blog-header">
<h1>{{ frontmatter.title || '博客' }}</h1>
<p v-if="frontmatter.subtitle">{{ frontmatter.subtitle }}</p>
</header>
<div class="filters">
<input
v-model="searchQuery"
type="text"
placeholder="搜索文章..."
class="search-input"
/>
<div class="tag-filter">
<button
:class="{ active: !selectedTag }"
@click="selectedTag = null"
>
全部
</button>
<button
v-for="tag in allTags"
:key="tag"
:class="{ active: selectedTag === tag }"
@click="selectedTag = tag"
>
{{ tag }}
</button>
</div>
</div>
<div class="posts">
<article
v-for="post in filteredPosts"
:key="post.path"
class="post-card"
@click="navigate(post.path)"
>
<div class="post-meta">
<time>{{ post.date }}</time>
<span v-if="post.readingTime">{{ post.readingTime }} 分钟</span>
</div>
<h2 class="post-title">{{ post.title }}</h2>
<p class="post-excerpt">{{ post.excerpt }}</p>
<div class="post-tags">
<span v-for="tag in post.tags" :key="tag" class="tag">
{{ tag }}
</span>
</div>
</article>
</div>
<div v-if="filteredPosts.length === 0" class="empty">
<p>没有找到匹配的文章</p>
</div>
</div>
</template>
<style scoped>
.blog-layout {
max-width: 800px;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.blog-header {
text-align: center;
margin-bottom: 2rem;
}
.blog-header h1 {
font-size: 2rem;
margin-bottom: 0.5rem;
}
.filters {
margin-bottom: 2rem;
}
.search-input {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
margin-bottom: 1rem;
font-size: 1rem;
}
.tag-filter {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tag-filter button {
padding: 0.25rem 0.75rem;
background: var(--vp-c-bg-alt);
border: 1px solid var(--vp-c-divider);
border-radius: 16px;
cursor: pointer;
font-size: 0.875rem;
transition: all 0.2s;
}
.tag-filter button.active {
background: var(--vp-c-brand-1);
color: white;
border-color: var(--vp-c-brand-1);
}
.post-card {
padding: 1.5rem;
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
margin-bottom: 1rem;
cursor: pointer;
transition: all 0.2s;
}
.post-card:hover {
border-color: var(--vp-c-brand-1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.post-meta {
display: flex;
gap: 1rem;
font-size: 0.85rem;
color: var(--vp-c-text-3);
margin-bottom: 0.5rem;
}
.post-title {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.post-excerpt {
color: var(--vp-c-text-2);
font-size: 0.9rem;
margin-bottom: 0.75rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.post-tags {
display: flex;
gap: 0.5rem;
}
.tag {
font-size: 0.75rem;
padding: 0.125rem 0.5rem;
background: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
border-radius: 4px;
}
.empty {
text-align: center;
padding: 4rem 0;
color: var(--vp-c-text-2);
}
</style>步骤 2:创建文章模板
markdown
<!-- docs/blog/my-post.md -->
---
title: 我的第一篇博客
date: 2026-04-04
tags:
- VitePress
- 教程
readingTime: 5
excerpt: 这是一篇关于如何在 VitePress 中添加博客功能的教程。
---
# 我的第一篇博客
文章内容...
## 简介
...
## 正文
...步骤 3:生成文章数据
创建脚本在构建时生成文章列表:
ts
// scripts/generate-blog-data.ts
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
interface Post {
title: string
path: string
date: string
tags: string[]
excerpt: string
readingTime: number
}
const posts: Post[] = []
function walk(dir: string) {
const entries = fs.readdirSync(dir, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
walk(fullPath)
} else if (entry.name.endsWith('.md') && entry.name !== 'index.md') {
const content = fs.readFileSync(fullPath, 'utf-8')
const { data, content: body } = matter(content)
if (!data.date) continue
posts.push({
title: data.title || entry.name,
path: '/' + fullPath.replace('docs/', '').replace('.md', ''),
date: data.date,
tags: data.tags || [],
excerpt: data.excerpt || body.slice(0, 200),
readingTime: data.readingTime || Math.ceil(body.length / 300)
})
}
}
}
walk('docs/blog')
// 按日期排序
posts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
// 保存数据
fs.writeFileSync('docs/public/posts.json', JSON.stringify(posts, null, 2))
console.log(`✅ 生成 ${posts.length} 篇文章数据`)步骤 4:创建博客首页
markdown
<!-- docs/blog/index.md -->
---
layout: ../../.vitepress/theme/layouts/BlogLayout.vue
title: 博客
subtitle: 分享技术心得与学习笔记
---
<script setup>
import { ref, onMounted } from 'vue'
const posts = ref([])
onMounted(async () => {
const res = await fetch('/posts.json')
posts.value = await res.json()
})
</script>步骤 5:添加 RSS 支持
bash
npm install feed -Dts
// scripts/generate-rss.ts
import fs from 'fs'
import { Feed } from 'feed'
const feed = new Feed({
title: '我的博客',
description: '技术博客',
id: 'https://your-domain.com',
link: 'https://your-domain.com',
language: 'zh-CN',
copyright: 'Copyright 2026'
})
// 添加文章...
const posts = JSON.parse(fs.readFileSync('docs/public/posts.json', 'utf-8'))
posts.forEach(post => {
feed.addItem({
title: post.title,
id: `https://your-domain.com${post.path}`,
link: `https://your-domain.com${post.path}`,
date: new Date(post.date),
description: post.excerpt
})
})
fs.writeFileSync('docs/public/rss.xml', feed.rss2())学习检查清单
- [ ] 创建了博客列表布局
- [ ] 实现了标签筛选
- [ ] 添加了搜索功能
- [ ] 生成了文章数据
- [ ] 添加了 RSS 订阅
下一步
继续学习 集成评论系统,为博客添加互动功能。