Skip to content

ビルド時のデータの読み込み

VitePress には データローダー (data loaders) という機能があり、任意のデータを読み込み、ページやコンポーネントからインポートすることができます。データの読み込みは ビルド時のみ 実行され、最終的な 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 を直接エクスポートしていないことに気づくでしょう。これは VitePress が裏側で load() を呼び出し、その結果を暗黙的に data として公開しているためです。

ローダーが非同期でも動作します。

js
export default {
  async load() {
    // リモートデータを取得
    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) {
    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 ファイルにのみ対応し、それ以外のファイルは無視されます。

読み込まれるデータは ContentData[] 型の配列です。

ts
interface ContentData {
  url: string // ページのマッピング URL(例: /posts/hello.html)
  frontmatter: Record<string, any> // ページのフロントマター

  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>

オプション

デフォルトデータが要件に合わない場合は、オプションで変換処理を追加できます。

posts.data.js
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 ブログ での使用例も参考になります。

createContentLoader API は ビルドフック 内でも利用可能です。

.vitepress/config.js
js
export default {
  async buildEnd() {
    const posts = await createContentLoader('posts/*.md').load()
    // メタデータを基にファイルを生成(例: RSS フィード)
  }
}

型定義

ts
interface ContentOptions<T = ContentData[]> {
  includeSrc?: boolean
  render?: boolean
  excerpt?:
    | boolean
    | ((file: { data: { [key: string]: any }; content: string; excerpt?: string }, options?: any) => void)
    | string
  transform?: (data: ContentData[]) => T | Promise<T>
}

型付きデータローダー

TypeScript を使用する場合は、ローダーと data エクスポートを型付けできます。

ts
import { defineLoader } from 'vitepress'

export interface Data {
  // データ型
}

declare const data: Data
export { data }

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

設定情報の取得

ローダー内で設定情報を取得するには次のようにします。

ts
import type { SiteConfig } from 'vitepress'

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

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