# Загрузка данных в режиме реального времени {#build-time-data-loading} VitePress предоставляет функцию **загрузчиков данных**, которая позволяет загружать произвольные данные и импортировать их со страниц или компонентов. Загрузка данных выполняется **только во время сборки**: Полученные данные будут сериализованы в виде JSON в финальной сборке JavaScript. Загрузчики данных могут использоваться для получения удалённых данных или генерирования метаданных на основе локальных файлов. Например, вы можете использовать загрузчики данных для анализа всех локальных страниц API и автоматического создания индекса всех записей API. ## Пример использования {#basic-usage} Файл загрузчика данных должен заканчиваться либо `.data.js`, либо `.data.ts`. Файл должен предоставлять экспорт объекта по умолчанию с помощью метода `load()`: ```js // example.data.js export default { load() { return { hello: 'мир' } } } ``` Модуль загрузчика выполняется только в Node.js, поэтому вы можете импортировать любые API Node и зависимости npm по мере необходимости. Затем вы можете импортировать данные из этого файла в страницы `.md` и компоненты `.vue` с помощью экспорта с именем `data`: ```vue
{{ data }}
``` Результат: ```json { "hello": "мир" } ``` Вы заметите, что сам загрузчик данных не экспортирует `data`. Это VitePress вызывает метод `load()` за кулисами и неявно раскрывает результат через именованный экспорт `data`. Это работает, даже если загрузчик асинхронный: ```js export default { async load() { // получение удалённых данных return (await fetch('...')).json() } } ``` ## Данные из локальных файлов {#data-from-local-files} Если вам нужно генерировать данные на основе локальных файлов, используйте опцию `watch` в загрузчике данных, чтобы изменения, внесённые в эти файлы, вызывали горячие обновления. Опция `watch` удобна ещё и тем, что вы можете использовать [шаблоны glob](https://github.com/mrmlnc/fast-glob#pattern-syntax) для соответствия нескольким файлам. Шаблоны могут быть относительными к самому файлу загрузчика, а функция `load()` будет получать совпадающие файлы в виде абсолютных путей. В следующем примере показана загрузка CSV-файлов и их преобразование в JSON с помощью [csv-parse](https://github.com/adaltas/node-csv/tree/master/packages/csv-parse/). Поскольку этот файл выполняется только во время сборки, вы не будете передавать CSV-парсер клиенту! ```js 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` {#createcontentloader} При создании сайта, ориентированного на контент, нам часто приходится создавать страницы типа «архив», или «индекс», на которых мы перечисляем все доступные записи в нашей коллекции контента. Например, записи в блоге или страницы API. Мы **можем** реализовать это напрямую с помощью API загрузчика данных, но поскольку это очень распространённый случай использования, VitePress также предоставляет функцию `createContentLoader`, чтобы упростить эту задачу: ```js // posts.data.js import { createContentLoader } from 'vitepress' export default createContentLoader('posts/*.md' /* параметры */) ``` Эта функция принимает шаблон glob относительно [исходного каталога](./routing#source-directory) и возвращает объект `{ watch, load }` загрузчика данных, который может быть использован в качестве экспорта по умолчанию в файле загрузчика данных. В нем также реализовано кэширование на основе временных меток изменения файлов для повышения производительности dev. Обратите внимание, что загрузчик работает только с файлами Markdown — совпадающие файлы, не относящиеся к Markdown, будут пропущены. Загруженные данные будут представлять собой массив с типом `ContentData[]`: ```ts interface ContentData { // отображаемый URL-адрес страницы, например: /posts/hello.html (не включает `base`) // выполните итерацию вручную или используйте пользовательскую `трансформацию` для нормализации путей url: string // метаданные страницы frontmatter: Record // следующие параметры присутствуют только в том случае, если соответствующие опции включены // мы рассмотрим их ниже src: string | undefined html: string | undefined excerpt: string | undefined } ``` По умолчанию указываются только `url` и `frontmatter`. Это связано с тем, что загруженные данные будут вложены в клиентский пакет в виде JSON, поэтому нам нужно быть осторожными с их размером. Вот пример использования этих данных для создания минимальной индексной страницы блога: ```vue ``` ### Параметры {#options} Данные по умолчанию могут не соответствовать всем требованиям — вы можете изменить данные с помощью параметров: ```js // 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](https://github.com/vuejs/blog/blob/main/.vitepress/theme/posts.data.ts). API `createContentLoader` также можно использовать внутри [хуков сборки](../reference/site-config#build-hooks): ```js // .vitepress/config.js export default { async buildEnd() { const posts = await createContentLoader('posts/*.md').load() // генерируем файлы на основе метаданных сообщений, например, RSS-канал } } ``` **Types** ```ts interface ContentOptions { /** * Включаем 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. */ transform?: (data: ContentData[]) => T | Promise } ``` ## Загрузчики типизированных данных {#typed-data-loaders} При использовании TypeScript вы можете ввести свой загрузчик и экспортировать `data` следующим образом: ```ts import { defineLoader } from 'vitepress' export interface Data { // тип данных } declare const data: Data export { data } export default defineLoader({ // тип проверенных опций загрузчика watch: ['...'], async load(): Promise { // ... } }) ``` ## Конфигурация {#configuration} Чтобы получить информацию о конфигурации внутри загрузчика, вы можете использовать код, подобный этому: ```ts import type { SiteConfig } from 'vitepress' const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG ```