コンテンツへスキップ

ビルド時データ読み込み

VitePressは、任意のデータを読み込んでページやコンポーネントからインポートできる**データローダー**という機能を提供します。データ読み込みは**ビルド時のみ**実行され、結果のデータは最終的なJavaScriptバンドルにJSONとしてシリアライズされます。

データローダーは、リモートデータの取得や、ローカルファイルに基づくメタデータの生成に使用できます。たとえば、ローカルのすべてのAPIページを解析し、すべてのAPIエントリのインデックスを自動的に生成するためにデータローダーを使用できます。

基本的な使用方法

データローダーファイルは、.data.jsまたは.data.tsで終わる必要があります。ファイルは、load()メソッドを持つオブジェクトのデフォルトエクスポートを提供する必要があります。

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

ローダーモジュールはNode.jsでのみ評価されるため、必要に応じてNode APIとnpm依存関係をインポートできます。

その後、data名前付きエクスポートを使用して、このファイルから.mdページと.vueコンポーネントでデータを読み込むことができます。

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

<pre>{{ data }}</pre>

出力

json
{
  "hello": "world"
}

データローダー自体はdataをエクスポートしないことに注意してください。VitePressが内部でload()メソッドを呼び出し、data名前付きエクスポートを介して結果を暗黙的に公開します。

これは、ローダーが非同期の場合でも機能します。

js
export default {
  async load() {
    // fetch remote data
    return (await fetch('...')).json()
  }
}

ローカルファイルからのデータ

ローカルファイルに基づいてデータを作成する必要がある場合は、データローダーでwatchオプションを使用し、これらのファイルに加えられた変更によってホットアップデートをトリガーできるようにする必要があります。

watchオプションは、globパターンを使用して複数のファイルに一致させることができるため便利です。パターンはローダーファイル自体を基準にでき、load()関数は一致するファイルを参照する絶対パスを受け取ります。

次の例は、CSVファイルを読み込んでcsv-parseを使用してJSONに変換する方法を示しています。このファイルはビルド時のみ実行されるため、クライアントにCSVパーサーは配信されません!

js
import fs from 'node:fs'
import { parse } from 'csv-parse/sync'

export default {
  watch: ['./data/*.csv'],
  load(watchedFiles) {
    // watchedFiles will be an array of absolute paths of the matched files.
    // generate an array of blog post metadata that can be used to render
    // a list in the theme layout
    return watchedFiles.map((file) => {
      return parse(fs.readFileSync(file, 'utf-8'), {
        columns: true,
        skip_empty_lines: true
      })
    })
  }
}

createContentLoader

コンテンツ中心のサイトを構築する場合、多くの場合、「アーカイブ」や「インデックス」ページを作成する必要があります。たとえば、ブログ投稿やAPIページなど、コンテンツコレクション内のすべての利用可能なエントリをリストするページです。これはデータローダーAPIを使用して直接実装できますが、これは非常に一般的なユースケースであるため、VitePressはこれを簡素化するためのcreateContentLoaderヘルパーも提供します。

js
// posts.data.js
import { createContentLoader } from 'vitepress'

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

このヘルパーは、ソースディレクトリを基準としたglobパターンを受け取り、データローダーファイルのデフォルトエクスポートとして使用できる{ watch, load }データローダーオブジェクトを返します。また、ファイルの変更時刻に基づいてキャッシングを実装して、開発パフォーマンスを向上させます。

ローダーはMarkdownファイルでのみ機能することに注意してください。一致しないMarkdownファイルはスキップされます。

読み込まれたデータは、ContentData[]型の配列になります。

ts
interface ContentData {
  // mapped URL for the page. e.g. /posts/hello.html (does not include base)
  // manually iterate or use custom `transform` to normalize the paths
  url: string
  // frontmatter data of the page
  frontmatter: Record<string, any>

  // the following are only present if relevant options are enabled
  // we will discuss them below
  src: string | undefined
  html: string | undefined
  excerpt: string | undefined
}

デフォルトでは、urlfrontmatterのみが提供されます。これは、読み込まれたデータがクライアントバンドルにJSONとしてインライン化されるため、そのサイズに注意する必要があるためです。最小限のブログインデックスページを作成するためにデータを使用する例を次に示します。

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>

オプション

デフォルトのデータはすべてのニーズに適しているとは限りません。オプションを使用してデータを変換することができます。

js
// posts.data.js
import { createContentLoader } from 'vitepress'

export default createContentLoader('posts/*.md', {
  includeSrc: true, // include raw markdown source?
  render: true,     // include rendered full page HTML?
  excerpt: true,    // include excerpt?
  transform(rawData) {
    // map, sort, or filter the raw data as you wish.
    // the final result is what will be shipped to the client.
    return rawData.sort((a, b) => {
      return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)
    }).map((page) => {
      page.src     // raw markdown source
      page.html    // rendered full page HTML
      page.excerpt // rendered excerpt HTML (content above first `---`)
      return {/* ... */}
    })
  }
})

Vue.jsブログでの使用方法を確認してください。

createContentLoader APIは、ビルドフック内でも使用できます。

js
// .vitepress/config.js
export default {
  async buildEnd() {
    const posts = await createContentLoader('posts/*.md').load()
    // generate files based on posts metadata, e.g. RSS feed
  }
}

ts
interface ContentOptions<T = ContentData[]> {
  /**
   * Include src?
   * @default false
   */
  includeSrc?: boolean

  /**
   * Render src to HTML and include in data?
   * @default false
   */
  render?: boolean

  /**
   * If `boolean`, whether to parse and include excerpt? (rendered as HTML)
   *
   * If `function`, control how the excerpt is extracted from the content.
   *
   * If `string`, define a custom separator to be used for extracting the
   * excerpt. Default separator is `---` if `excerpt` is `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

  /**
   * Transform the data. Note the data will be inlined as JSON in the client
   * bundle if imported from components or markdown files.
   */
  transform?: (data: ContentData[]) => T | Promise<T>
}

型付きデータローダー

TypeScriptを使用する場合は、ローダーとdataエクスポートを次のように型指定できます。

ts
import { defineLoader } from 'vitepress'

export interface Data {
  // data type
}

declare const data: Data
export { data }

export default defineLoader({
  // type checked loader options
  watch: ['...'],
  async load(): Promise<Data> {
    // ...
  }
})

設定

ローダー内で設定情報を取得するには、次のようなコードを使用できます。

ts
import type { SiteConfig } from 'vitepress'

const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG

MITライセンスの下でリリースされています。