数据加载
VitePress 提供了一个数据加载功能,允许你加载任意数据并从页面或组件中导入它。数据加载只在构建时执行:生成的数据将作为 JSON 序列化在最终的 JavaScript 包中。
数据加载可以用来获取远程数据,也可以基于本地文件生成元数据。例如,你可以使用数据加载来解析所有本地 API 页面并自动生成所有 API 条目的索引。
基本用法
数据加载文件必须以 .data.js
或 .data.ts
结尾。该文件应该提供一个默认导出的对象,该对象具有 load()
方法:
// example.data.js
export default {
load() {
return {
hello: 'world'
}
}
}
数据加载模块只在 Node.js 中评估,因此你可以根据需要导入 Node.js API 和 npm 依赖项。
然后你可以使用 data
命名导出从任何页面导入此文件中的数据:
<script setup>
import { data } from './example.data.js'
</script>
<pre>{{ data }}</pre>
输出:
{
"hello": "world"
}
你会注意到数据加载器本身不导出 data
。这是因为 VitePress 在幕后调用 load()
方法,并通过名为 data
的命名导出隐式公开结果。
即使加载器是异步的,这也有效:
// example.data.js
export default {
async load() {
// 加载远程数据
return (await fetch('...')).json()
}
}
从本地文件生成数据
当你需要基于本地文件生成数据时,你应该在数据加载器中使用 watch
选项,以便这些文件更改时可以触发热更新。
watch
选项也很方便,因为你可以使用 glob 模式 来匹配多个文件。模式可以是相对于加载器文件本身的,它们将相对于项目根目录解析。
// posts.data.js
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
watch: ['./posts/**/*.md'],
load() {
// 返回从本地文件生成的数据
}
})
createContentLoader
当构建以内容为中心的站点时,我们经常需要创建一个"存档"或"索引"页面:一个我们可以列出内容集合中所有可用条目的页面,例如博客文章或 API 页面。我们可以直接使用数据加载 API 来实现这一点,但由于这是一个常见的用例,VitePress 还提供了一个 createContentLoader
辅助程序来简化这一点:
// posts.data.js
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md') // 包括所有 `posts/*.md`
<script setup>
import { data as posts } from './posts.data.js'
</script>
<template>
<h1>All Blog Posts</h1>
<ul>
<li v-for="post of posts" :key="post.url">
<a :href="post.url">{{ post.frontmatter.title }}</a>
<span>by {{ post.frontmatter.author }}</span>
</li>
</ul>
</template>
辅助程序接受一个 glob 模式作为第一个参数,并返回一个 { url, frontmatter, excerpt }
对象数组。excerpt 是渲染的内容摘要(第一个 ---
分隔符之前的内容)。
声明监视文件
默认情况下,内容加载器将添加所有匹配的文件作为监视依赖项。这意味着对匹配文件的任何更改都将触发热重载。但是,在大型项目中,这可能会降低开发性能,特别是如果加载器包含大量文件。在这种情况下,你可以禁用监视并手动声明监视文件:
// posts.data.js
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
includeSrc: false, // 不包括原始 markdown 源
render: false, // 不包括渲染的完整页面 HTML
excerpt: false, // 不包括摘要
watch: false // 禁用自动监视,手动声明监视文件
})
高级配置
createContentLoader
还接受一个选项对象作为第二个参数:
interface ContentOptions<T = ContentData[]> {
/**
* 包括 src?
* @default false
*/
includeSrc?: boolean
/**
* 渲染 src 到 HTML 并包括在数据中?
* @default false
*/
render?: boolean
/**
* 如果 `boolean`,是否解析和包括摘要?(渲染为 HTML)
*
* 如果 `function`,控制如何从内容中提取摘要。
*
* 如果 `string`,定义用于提取摘要的自定义分隔符。
* 默认分隔符是 `---`,如果你在 frontmatter 中使用它。
*
* @default false
*/
excerpt?:
| boolean
| ((file: { data: { [key: string]: any }; content: string; filename: string; excerpt?: string }) => string)
| string
/**
* 要监视的附加文件。
*
* 监视的文件路径相对于项目根目录。
*/
watch?: string | string[]
/**
* 转换数据。注意数据将在客户端内联,
* 所以保持有效负载尽可能小。
*/
transform?: (data: ContentData[]) => T | Promise<T>
}
例如,要仅包括页面标题和 URL:
// posts.data.js
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
includeSrc: false, // 不包括原始 markdown 源
render: false, // 不包括渲染的完整页面 HTML
excerpt: false, // 不包括摘要
transform(rawData) {
// 根据原始数据映射,排序,或过滤
// 最终结果是将发送给客户端的内容
return rawData.sort((a, b) => {
return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)
}).map((page) => {
return {
title: page.frontmatter.title,
url: page.url
}
})
}
})
注意 transform
钩子不会在开发服务器中运行。但是,如果你需要在开发中预览结果,你可以添加 console.log(data)
来检查转换后的数据。
类型化数据加载器
使用 TypeScript 时,你可以像这样类型化加载器和 data
导出:
// posts.data.ts
import { defineLoader } from 'vitepress'
export interface Post {
title: string
url: string
date: {
time: number
string: string
}
}
declare const data: Post[]
export { data }
export default defineLoader({
async load(): Promise<Post[]> {
// ...
}
})
配置
要在配置文件中获取数据,你可以使用 loadData
辅助程序:
// .vitepress/config.js
import { defineConfig, loadData } from 'vitepress'
const posts = loadData('posts.data.js')
export default defineConfig({
// ...
themeConfig: {
sidebar: [
// ...
{
text: 'Blog Posts',
items: posts.map((post) => ({
text: post.frontmatter.title,
link: post.url
}))
}
]
}
})