ビルド時のデータの読み込み
VitePress には データローダー (data loaders) という機能があり、任意のデータを読み込み、ページやコンポーネントからインポートすることができます。データの読み込みは ビルド時のみ 実行され、最終的な JavaScript バンドルには JSON としてシリアライズされたデータが含まれます。
データローダーはリモートデータの取得や、ローカルファイルに基づいたメタデータの生成に利用できます。たとえば、ローカルの API ページをすべて解析して API エントリのインデックスを自動生成するといったことが可能です。
基本的な使い方
データローダーファイルは .data.js
または .data.ts
で終わる必要があります。ファイルは load()
メソッドを持つオブジェクトをデフォルトエクスポートします。
export default {
load() {
return {
hello: 'world'
}
}
}
ローダーモジュールは Node.js 上でのみ評価されるため、Node API や 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 ファイルを読み込み csv-parse を使って JSON に変換する例です。このコードはビルド時にのみ実行されるため、CSV パーサーがクライアントに送られることはありません。
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
というヘルパーが用意されています。
import { createContentLoader } from 'vitepress'
export default createContentLoader('posts/*.md', /* options */)
このヘルパーは ソースディレクトリ からの相対 glob パターンを受け取り、{ watch, load }
を返すデータローダーオブジェクトを生成します。これをデータローダーファイルのデフォルトエクスポートにできます。開発時のパフォーマンスを向上させるために、ファイルの更新時刻に基づくキャッシュも行われます。
このローダーは Markdown ファイルにのみ対応し、それ以外のファイルは無視されます。
読み込まれるデータは ContentData[]
型の配列です。
interface ContentData {
url: string // ページのマッピング URL(例: /posts/hello.html)
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>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>
オプション
デフォルトデータが要件に合わない場合は、オプションで変換処理を追加できます。
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 は ビルドフック 内でも利用可能です。
export default {
async buildEnd() {
const posts = await createContentLoader('posts/*.md').load()
// メタデータを基にファイルを生成(例: RSS フィード)
}
}
型定義
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
エクスポートを型付けできます。
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