Skip to content

数据加载

VitePress 提供了多种方式来加载和处理数据。

数据加载器

创建数据加载器

docs/.vitepress/ 目录下创建数据文件:

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

export default createContentLoader('posts/*.md', {
  transform(raw): Post[] {
    return raw
      .map(({ url, frontmatter }) => ({
        title: frontmatter.title,
        url,
        date: frontmatter.date,
        excerpt: frontmatter.excerpt
      }))
      .sort((a, b) => +new Date(b.date) - +new Date(a.date))
  }
})

interface Post {
  title: string
  url: string
  date: string
  excerpt: string
}

使用数据

vue
<!-- docs/posts/index.md -->
<script setup>
import { data as posts } from '.vitepress/data/posts.data'

const recentPosts = posts.slice(0, 5)
</script>

# 最新文章

<ul>
  <li v-for="post in recentPosts" :key="post.url">
    <a :href="post.url">{{ post.title }}</a>
    <span>{{ post.date }}</span>
  </li>
</ul>

createContentLoader API

基础用法

ts
import { createContentLoader } from 'vitepress'

export default createContentLoader('posts/*.md')

配置选项

ts
export default createContentLoader('posts/*.md', {
  // 包含 src 和 excerpt
  includeSrc: true,
  
  // 渲染 excerpt 为 HTML
  render: true,
  
  // excerpt 分隔符
  excerpt: '<!-- more -->',
  
  // 转换函数
  transform(raw) {
    return raw.map(post => ({
      ...post,
      // 自定义处理
    }))
  }
})

返回数据结构

ts
interface ContentData {
  url: string           // 页面 URL
  frontmatter: object   // Frontmatter 数据
  excerpt?: string      // 摘要
  src?: string          // 原始内容
  html?: string         // 渲染后的 HTML
}

静态数据文件

JSON 数据

json
// docs/data/authors.json
[
  { "name": "张三", "avatar": "/avatars/zhangsan.png" },
  { "name": "李四", "avatar": "/avatars/lisi.png" }
]

在组件中使用:

vue
<script setup>
import authors from '../data/authors.json'
</script>

<ul>
  <li v-for="author in authors" :key="author.name">
    <img :src="author.avatar" :alt="author.name" />
    {{ author.name }}
  </li>
</ul>

TypeScript 数据

ts
// docs/data/links.ts
export interface Link {
  title: string
  url: string
  icon?: string
}

export const links: Link[] = [
  { title: 'GitHub', url: 'https://github.com', icon: 'github' },
  { title: 'Twitter', url: 'https://twitter.com', icon: 'twitter' }
]

使用:

vue
<script setup lang="ts">
import { links } from '../data/links'
</script>

<a v-for="link in links" :key="link.url" :href="link.url">
  {{ link.title }}
</a>

运行时 API

useData

获取站点和页面数据:

ts
import { useData } from 'vitepress'

const {
  site,      // 站点级数据
  page,      // 当前页面数据
  frontmatter,
  themeConfig,
  lang,
  title,
  description,
  isDark
} = useData()

useRoute

获取当前路由信息:

ts
import { useRoute } from 'vitepress'

const route = useRoute()
// route.path - 当前路径
// route.component - 当前组件

useRouter

访问路由器:

ts
import { useRouter } from 'vitepress'

const router = useRouter()

function navigate() {
  router.go('/guide/')
}

构建时数据获取

从 API 获取数据

ts
// docs/.vitepress/data/github.data.ts
export default {
  async load() {
    const res = await fetch('https://api.github.com/repos/vuejs/vitepress')
    return res.json()
  }
}

从文件系统读取

ts
// docs/.vitepress/data/posts.data.ts
import { readFileSync, readdirSync } from 'fs'
import { join } from 'path'

export default {
  load() {
    const dir = join(__dirname, '../../posts')
    const files = readdirSync(dir)
    
    return files
      .filter(f => f.endsWith('.md'))
      .map(f => ({
        filename: f,
        content: readFileSync(join(dir, f), 'utf-8')
      }))
  }
}

博客示例

文章数据

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

export default createContentLoader('blog/*.md', {
  transform(raw) {
    return raw
      .map(({ url, frontmatter, excerpt }) => ({
        title: frontmatter.title,
        url,
        date: formatDate(frontmatter.date),
        author: frontmatter.author,
        tags: frontmatter.tags || [],
        excerpt
      }))
      .sort((a, b) => +new Date(b.date) - +new Date(a.date))
  }
})

function formatDate(date: string) {
  return new Date(date).toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  })
}

博客首页

markdown
---
title: 博客
---

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

# 博客文章

<div v-for="post in posts" :key="post.url" class="post-card">
  <h2><a :href="post.url">{{ post.title }}</a></h2>
  <p class="meta">{{ post.date }} · {{ post.author }}</p>
  <p>{{ post.excerpt }}</p>
  <div class="tags">
    <span v-for="tag in post.tags" :key="tag" class="tag">{{ tag }}</span>
  </div>
</div>

下一步

学习 多语言支持 来构建国际化站点。

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献