跳转到内容

构建时数据加载

VitePress 提供了数据加载的功能,它允许加载任意数据并从页面或组件中导入它。数据加载只在构建时执行:最终的数据将被序列化为 JavaScript 包中的 JSON。

数据加载可以被用于获取远程数据,也可以基于本地文件生成元数据。例如,可以使用数据加载来解析所有本地 API 页面并自动生成所有 API 入口的索引。

基本用法

一个用于数据加载的文件必须以 .data.js.data.ts 结尾。该文件应该提供一个默认导出的对象,该对象具有 load() 方法:

example.data.js
js
export default {  load() {  return {  hello: 'world'  }  } }

数据加载模块只在 Node.js 中执行,因此可以按需导入 Node API 和 npm 依赖。

然后,可以在 .md 页面和 .vue 组件中使用 data 具名导出从该文件中导入数据:

vue
<script setup> import { data } from './example.data.js' </script>  <pre>{{ data }}</pre>

输出:

json
{  "hello": "world" }

你会注意到 data loader 本身并没有导出 data。这是因为 VitePress 在后台调用了 load() 方法,并通过名为 data 的具名导出隐式地暴露了结果。

即使它是异步的,这也是有效的:

js
export default {  async load() {  // 获取远程数据  return (await fetch('...')).json()  } }

使用本地文件生成数据

当需要基于本地文件生成数据时,需要在 data loader 中使用 watch 选项,以便这些文件改动时可以触发热更新。

watch 选项也很方便,因为可以使用 glob 模式 匹配多个文件。模式可以相对于数据加载文件本身,load() 函数将接收匹配文件的绝对路径。

下面的例子展示了如何使用 csv-parse 加载 CSV 文件并将其转换为 JSON。因为此文件仅在构建时执行,因此不会将 CSV 解析器发送到客户端。

js
import fs from 'node:fs' import { parse } from 'csv-parse/sync'  export default {  watch: ['./data/*.csv'],  load(watchedFiles) {  // watchFiles 是一个所匹配文件的绝对路径的数组。  // 生成一个博客文章元数据数组  // 可用于在主题布局中呈现列表。  return watchedFiles.map((file) => {  return parse(fs.readFileSync(file, 'utf-8'), {  columns: true,  skip_empty_lines: true  })  })  } }

createContentLoader

当构建一个内容为主的站点时,我们经常需要创建一个“归档”或“索引”页面:一个我们可以列出内容中的所有可用条目的页面,例如博客文章或 API 页面。我们可以直接使用数据加载 API 实现这一点,但由于这会经常使用,VitePress 还提供了一个 createContentLoader 辅助函数来简化这个过程:

posts.data.js
js
import { createContentLoader } from 'vitepress'  export default createContentLoader('posts/*.md', /* options */)

该辅助函数接受一个相对于源目录的 glob 模式,并返回一个 { watch, load } 数据加载对象,该对象可以用作数据加载文件中的默认导出。它还基于文件修改时间戳实现了缓存以提高开发性能。

请注意,数据加载仅适用于 Markdown 文件——匹配的非 Markdown 文件将被跳过。

加载的数据将是一个类型为 ContentData[] 的数组:

ts
interface ContentData {  // 页面的映射 URL,如 /posts/hello.html(不包括 base)  // 手动迭代或使用自定义 `transform` 来标准化路径  url: string  // 页面的 frontmatter 数据  frontmatter: Record<string, any>   // 只有启用了相关选项,才会出现以下内容  // 我们将在下面讨论它们  src: string | undefined  html: string | undefined  excerpt: string | undefined }

默认情况下只提供 urlfrontmatter。这是因为加载的数据将作为 JSON 内联在客户端 bundle 中,我们需要谨慎考虑其大小。下面的例子展示了如何使用数据构建最小的博客索引页面:

vue
<script setup> import { data as posts } from './posts.data.js' </script>  <template>  <h1>All Blog Posts</h1>  <ul>  <li v-for="post of posts">  <a :href="post.url">{{ post.frontmatter.title }}</a>  <span>by {{ post.frontmatter.author }}</span>  </li>  </ul> </template>

选项

默认数据可能不适合所有需求——可以选择使用选项转换数据:

posts.data.js
js
import { createContentLoader } from 'vitepress'  export default createContentLoader('posts/*.md', {  includeSrc: true, // 包含原始 markdown 源?  render: true, // 包含渲染的整页 HTML?  excerpt: true, // 包含摘录?  transform(rawData) {  // 根据需要对原始数据进行 map、sort 或 filter  // 最终的结果是将发送给客户端的内容  return rawData.sort((a, b) => {  return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)  }).map((page) => {  page.src // 原始 markdown 源  page.html // 渲染的整页 HTML  page.excerpt // 渲染的摘录 HTML(第一个 `---` 上面的内容)  return {/* ... */}  })  } })

查看它在 Vue.js 博客中是如何使用的。

createContentLoader API 也可以在构建钩子中使用:

.vitepress/config.js
js
export default {  async buildEnd() {  const posts = await createContentLoader('posts/*.md').load()  // 根据 posts 元数据生成文件,如 RSS 订阅源  } }

类型

ts
interface ContentOptions<T = ContentData[]> {  /**  * 包含 src?  * @default false  */  includeSrc?: boolean   /**  * 将 src 渲染为 HTML 并包含在数据中?  * @default false  */  render?: boolean   /**  * 如果为 `boolean`,是否解析并包含摘录? (呈现为 HTML)  *  * 如果为 `function`,则控制如何从内容中提取摘录  *  * 如果为 `string`,则定义用于提取摘录的自定义分隔符  * 如果 `excerpt` 为 `true`,则默认分隔符为 `---`  *  * @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt  * @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt_separator  *  * @default false  */  excerpt?:  | boolean  | ((file: { data: { [key: string]: any }; content: string; excerpt?: string }, options?: any) => void)  | string   /**  * 转换数据。请注意,如果从组件或 Markdown 文件导入,数据将以 JSON 形式内联到客户端包中  */  transform?: (data: ContentData[]) => T | Promise<T> }

为 data loader 导出类型

当使用 TypeScript 时,可以像这样为 loader 和 data 导出类型:

ts
import { defineLoader } from 'vitepress'  export interface Data {  // data 类型 }  declare const data: Data export { data }  export default defineLoader({  // 类型检查加载器选项  watch: ['...'],  async load(): Promise<Data> {  // ...  } })

配置

要获取 data loader 中的配置信息,可以使用如下代码:

ts
import type { SiteConfig } from 'vitepress'  const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG