Загрузка данных во время сборки
VitePress предоставляет функцию, называемую загрузчиками данных, которая позволяет загружать произвольные данные и импортировать их из страниц или компонентов. Загрузка данных выполняется только во время сборки: полученные данные будут сериализованы в JSON в конечном JavaScript-бандле.
Загрузчики данных можно использовать для получения удаленных данных или генерации метаданных на основе локальных файлов. Например, вы можете использовать загрузчики данных для анализа всех ваших локальных страниц API и автоматической генерации индекса всех записей API.
Основное использование
Файл загрузчика данных должен заканчиваться либо на .data.js
, либо на .data.ts
. Файл должен предоставлять объект по умолчанию с методом load()
:
// example.data.js
export default {
load() {
return {
hello: 'world'
}
}
}
Модуль загрузчика выполняется только в Node.js, поэтому вы можете импортировать API Node и зависимости npm по мере необходимости.
Затем вы можете импортировать данные из этого файла в .md
страницы и .vue
компоненты, используя именованный экспорт data
:
<script setup>
import { data } from './example.data.js'
</script>
<pre>{{ data }}</pre>
Вывод:
{
"hello": "world"
}
Обратите внимание, что сам загрузчик данных не экспортирует data
. Это VitePress вызывает метод load()
за кулисами и неявно предоставляет результат через именованный экспорт data
.
Это работает даже если загрузчик асинхронный:
export default {
async load() {
// получение удаленных данных
return (await fetch('...')).json()
}
}
Данные из локальных файлов
Когда вам нужно сгенерировать данные на основе локальных файлов, вы должны использовать опцию watch
в загрузчике данных, чтобы изменения в этих файлах могли вызывать горячие обновления.
Опция watch
также удобна тем, что вы можете использовать шаблоны glob для сопоставления нескольких файлов. Шаблоны могут быть относительными по отношению к самому файлу загрузчика, и функция load()
получит сопоставленные файлы как абсолютные пути.
Следующий пример показывает загрузку файлов CSV и их преобразование в JSON с использованием csv-parse. Поскольку этот файл выполняется только во время сборки, вы не будете отправлять парсер CSV клиенту!
import fs from 'node:fs'
import { parse } from 'csv-parse/sync'
export default {
watch: ['./data/*.csv'],
load(watchedFiles) {
// watchedFiles будет массивом абсолютных путей сопоставленных файлов.
// генерируем массив метаданных блог-постов, который можно использовать для отображения
// списка в макете темы
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
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', /* options */)
Помощник принимает шаблон glob, относительный к директории исходников, и возвращает объект загрузчика данных { watch, load}
, который можно использовать как экспорт по умолчанию в файле загрузчика данных. Он также реализует кэширование на основе временных меток изменения файлов для улучшения производительности во время разработки.
Обратите внимание, что загрузчик работает только с файлами Markdown - не-Markdown файлы будут пропущены.
Загруженные данные будут массивом с типом ContentData[]
:
interface ContentData {
// сопоставленный URL для страницы, например, /posts/hello.html (не включает base)
// вручную итерируйте или используйте пользовательский `transform` для нормализации путей
url: string
// данные frontmatter страницы
frontmatter: Record<string, any>
// следующие присутствуют только если соответствующие опции включены
// мы обсудим их ниже
src: string | undefined
html: string | undefined
excerpt: string | undefined
}
По умолчанию предоставляются только url
и frontmatter
. Это потому, что загруженные данные будут встроены в JSON в клиентском бандле, поэтому мы должны быть осторожны с его размером. Вот пример использования данных для создания минимальной индексной страницы блога:
<script setup>
import { data as posts } from './posts.data.js'
</script>
<template>
<h1>Все блог-посты</h1>
<ul>
<li v-for="post in posts">
<a :href="post.url">{{ post.frontmatter.title }}</a>
<span>от {{ post.frontmatter.author }}</span>
</li>
</ul>
</template>
Опции
По умолчанию данные могут не соответствовать всем потребностям - вы можете включить преобразование данных с помощью опций:
// posts.data.js
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', {
includeSrc: true, // включать исходный код markdown?
render: true, // включать отрендеренный полный HTML страницы?
excerpt: true, // включать выдержку?
transform(rawData) {
// сортируйте, фильтруйте или преобразуйте сырые данные по своему усмотрению.
// окончательный результат будет отправлен клиенту.
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.
API createContentLoader
также может быть использован внутри хуков сборки:
// .vitepress/config.js
export default {
async buildEnd() {
const posts = await createContentLoader('posts/*.md').load()
// генерируем файлы на основе метаданных постов, например, RSS-канал
}
}
Типы
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
/**
* Преобразовать данные. Обратите внимание, что данные будут встроены в JSON в клиентском
* бандле, если импортированы из компонентов или файлов markdown.
*/
transform?: (data: ContentData[]) => T | Promise<T>
}
Типизированные загрузчики данных
При использовании TypeScript вы можете типизировать ваш загрузчик и экспорт data
следующим образом:
import { defineLoader } from 'vitepress'
export interface Data {
// тип данных
}
declare const data: Data
export { data }
export default defineLoader({
// опции загрузчика с проверкой типов
watch: ['...'],
async load(): Promise<Data> {
// ...
}
})
Конфигурация
Чтобы получить информацию о конфигурации внутри загрузчика, вы можете использовать код вроде этого:
import type { SiteConfig } from 'vitepress'
const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG