Skip to content

知识库站点

本教程介绍如何使用 VitePress 搭建一个个人知识库/笔记站点。

项目结构

knowledge-base/
├── docs/
│   ├── .vitepress/
│   │   ├── config.mts
│   │   └── data/
│   │       └── notes.data.ts
│   ├── notes/
│   │   ├── frontend/
│   │   │   ├── vue/
│   │   │   └── react/
│   │   ├── backend/
│   │   └── devops/
│   ├── daily/
│   │   └── 2024/
│   └── index.md
└── package.json

配置文件

ts
// docs/.vitepress/config.mts
import { defineConfig } from 'vitepress'

export default defineConfig({
  title: '我的知识库',
  description: '个人知识管理与笔记系统',
  
  themeConfig: {
    logo: '/logo.svg',
    
    nav: [
      { text: '首页', link: '/' },
      { 
        text: '笔记', 
        items: [
          { text: '前端开发', link: '/notes/frontend/' },
          { text: '后端开发', link: '/notes/backend/' },
          { text: '运维部署', link: '/notes/devops/' }
        ]
      },
      { text: '日常记录', link: '/daily/2024/' },
      { text: '标签', link: '/tags/' }
    ],
    
    sidebar: {
      '/notes/frontend/': [
        {
          text: '前端开发',
          items: [
            { text: '概述', link: '/notes/frontend/' },
            {
              text: 'Vue.js',
              collapsed: false,
              items: [
                { text: '基础概念', link: '/notes/frontend/vue/basics' },
                { text: '组合式 API', link: '/notes/frontend/vue/composition' }
              ]
            },
            {
              text: 'React',
              collapsed: true,
              items: [
                { text: '基础概念', link: '/notes/frontend/react/basics' }
              ]
            }
          ]
        }
      ],
      '/notes/backend/': [
        {
          text: '后端开发',
          items: [
            { text: '概述', link: '/notes/backend/' },
            { text: 'Node.js', link: '/notes/backend/nodejs' },
            { text: '数据库', link: '/notes/backend/database' }
          ]
        }
      ],
      '/daily/': [
        {
          text: '日常记录',
          items: [
            { text: '2024', link: '/daily/2024/' }
          ]
        }
      ]
    },
    
    search: {
      provider: 'local',
      options: {
        detailedView: true
      }
    },
    
    outline: {
      label: '目录',
      level: [2, 3]
    }
  }
})

笔记数据加载器

ts
// docs/.vitepress/data/notes.data.ts
import { createContentLoader } from 'vitepress'

export interface Note {
  title: string
  url: string
  date: string
  tags: string[]
  category: string
  excerpt: string
}

export default createContentLoader('notes/**/*.md', {
  excerpt: true,
  transform(raw): Note[] {
    return raw
      .map(({ url, frontmatter, excerpt }) => ({
        title: frontmatter.title || '无标题',
        url,
        date: frontmatter.date || '',
        tags: frontmatter.tags || [],
        category: frontmatter.category || '',
        excerpt
      }))
      .sort((a, b) => +new Date(b.date) - +new Date(a.date))
  }
})

笔记模板

笔记 Frontmatter

yaml
---
title: Vue 组合式 API 详解
date: 2024-01-15
tags:
  - Vue
  - 前端
category: 前端开发
author: 张三
---

笔记内容模板

markdown
---
title: 笔记标题
date: 2024-01-15
tags:
  - 标签1
  - 标签2
category: 分类
---

# 笔记标题

## 概述

简要描述笔记内容...

## 详细内容

### 要点一

详细说明...

### 要点二

详细说明...

## 代码示例

\`\`\`javascript
// 示例代码
\`\`\`

## 参考资料

- [参考链接1](url)
- [参考链接2](url)

## 相关笔记

- [[相关笔记1]]
- [[相关笔记2]]

标签页面

创建 docs/tags.md

markdown
---
title: 标签索引
---

<script setup>
import { data as notes } from '.vitepress/data/notes.data'
import { ref, computed } from 'vue'

const allTags = computed(() => {
  const tagMap = new Map()
  notes.forEach(note => {
    note.tags.forEach(tag => {
      if (!tagMap.has(tag)) {
        tagMap.set(tag, [])
      }
      tagMap.get(tag).push(note)
    })
  })
  return Array.from(tagMap.entries()).sort((a, b) => b[1].length - a[1].length)
})

const selectedTag = ref(null)
const filteredNotes = computed(() => {
  if (!selectedTag.value) return []
  return notes.filter(n => n.tags.includes(selectedTag.value))
})
</script>

<div class="tags-page">
  <h1>标签索引</h1>
  
  <div class="tags-cloud">
    <button 
      v-for="[tag, noteList] in allTags" 
      :key="tag"
      :class="['tag', { active: selectedTag === tag }]"
      @click="selectedTag = tag"
    >
      {{ tag }} <span class="count">{{ noteList.length }}</span>
    </button>
  </div>
  
  <div v-if="selectedTag" class="notes-list">
    <h2>{{ selectedTag }} 相关笔记</h2>
    <ul>
      <li v-for="note in filteredNotes" :key="note.url">
        <a :href="note.url">{{ note.title }}</a>
        <span class="date">{{ note.date }}</span>
      </li>
    </ul>
  </div>
</div>

<style>
.tags-page {
  padding: 20px 0;
}

.tags-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 32px;
}

.tag {
  padding: 8px 16px;
  background: var(--vp-c-default-soft);
  border: none;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.2s;
}

.tag:hover,
.tag.active {
  background: var(--vp-c-brand-1);
  color: white;
}

.count {
  opacity: 0.7;
  margin-left: 4px;
}

.notes-list ul {
  list-style: none;
  padding: 0;
}

.notes-list li {
  padding: 12px 0;
  border-bottom: 1px solid var(--vp-c-divider);
  display: flex;
  justify-content: space-between;
}

.date {
  color: var(--vp-c-text-2);
  font-size: 14px;
}
</style>

搜索功能增强

ts
export default defineConfig({
  themeConfig: {
    search: {
      provider: 'local',
      options: {
        detailedView: true,
        miniSearch: {
          searchOptions: {
            fuzzy: 0.2,
            prefix: true,
            boost: { title: 4, text: 2, titles: 1 }
          }
        }
      }
    }
  }
})

双向链接

创建简单的双向链接支持:

vue
<!-- docs/.vitepress/theme/components/WikiLink.vue -->
<script setup>
import { computed } from 'vue'

const props = defineProps({
  target: String
})

const link = computed(() => {
  // 将 [[笔记名]] 转换为链接
  return `/notes/${props.target.toLowerCase().replace(/\s+/g, '-')}`
})
</script>

<template>
  <a :href="link" class="wiki-link">{{ target }}</a>
</template>

<style>
.wiki-link {
  color: var(--vp-c-brand-1);
  background: var(--vp-c-brand-soft);
  padding: 2px 6px;
  border-radius: 4px;
}
</style>

最近更新组件

vue
<script setup>
import { data as notes } from '.vitepress/data/notes.data'

const recentNotes = notes.slice(0, 10)
</script>

<div class="recent-notes">
  <h3>最近更新</h3>
  <ul>
    <li v-for="note in recentNotes" :key="note.url">
      <a :href="note.url">{{ note.title }}</a>
    </li>
  </ul>
</div>

最佳实践

实践说明
分类清晰使用目录结构组织内容
标签管理为笔记添加多个标签便于查找
定期整理定期回顾和更新笔记内容
搜索优化利用搜索功能快速定位
链接关联使用双向链接连接相关知识

下一步

查看 站点统计 为站点添加访问统计。

🎯 进阶挑战

完成基础教程后,尝试以下挑战来提升你的技能:

挑战 1:Markdown 双向链接 ⭐⭐⭐⭐

目标:实现类似 Obsidian 的 [[笔记名]] 双向链接语法。

提示

  • 创建自定义 Markdown 插件
  • 解析 [[...]] 语法
  • 自动生成反向链接列表

参考思路

ts
// 自定义 Markdown 插件
const wikiLinkPlugin = (md) => {
  md.inline.ruler.before('link', 'wiki_link', (state, silent) => {
    // 解析 [[笔记名]] 语法
    const match = /^\[\[([^\]]+)\]\]/.exec(state.src.slice(state.pos))
    if (match) {
      // 创建链接 token
      // ...
    }
  })
}

挑战 2:知识图谱可视化 ⭐⭐⭐⭐⭐

目标:使用力导向图展示笔记之间的关系网络。

提示

  • 使用 D3.js 或 ECharts
  • 分析笔记间的链接关系
  • 实现交互式图谱

挑战 3:笔记全屏演示模式 ⭐⭐⭐

目标:将笔记转换为类似 PPT 的演示模式。

提示

  • ## 标题分割内容
  • 创建幻灯片切换动画
  • 添加键盘导航

挑战 4:笔记导入导出 ⭐⭐⭐

目标:支持从其他笔记工具导入,以及导出为多种格式。

提示

  • 支持 Markdown 文件批量导入
  • 支持从 Notion/Obsidian 导入
  • 导出为 PDF/HTML

挑战 5:笔记版本历史 ⭐⭐⭐⭐

目标:记录笔记的修改历史,支持版本对比和回滚。

提示

  • 使用 Git 历史记录
  • 或自定义版本存储
  • 实现差异对比展示

挑战 6:智能笔记推荐 ⭐⭐⭐⭐⭐

目标:基于内容相似度推荐相关笔记。

提示

  • 提取笔记关键词
  • 计算相似度分数
  • 在页面底部展示推荐

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献