Skip to content

MarkdownでVueを使う

VitePress では、各 Markdown ファイルはまず HTML にコンパイルされ、その後 Vue の単一ファイルコンポーネント(SFC) として処理されます。つまり、Markdown 内で Vue のあらゆる機能が使えます。動的テンプレート、Vue コンポーネントの利用、<script> タグを追加してページ内ロジックを書くことも可能です。

なお、VitePress は Vue のコンパイラを利用して、Markdown コンテンツの純粋に静的な部分を自動検出・最適化します。静的コンテンツは単一のプレースホルダノードに最適化され、初回訪問時の JavaScript ペイロードから除外されます。クライアント側のハイドレーションでもスキップされます。要するに、そのページで動的な部分に対してだけコストを支払うことになります。

SSR 互換性

Vue の使用は SSR 互換である必要があります。詳細と一般的な回避策は SSR 互換性 を参照してください。

テンプレート記法

補間

各 Markdown は最初に HTML にコンパイルされ、その後 Vite の処理パイプラインで Vue コンポーネントとして扱われます。つまり、テキスト内で Vue 風の補間が使えます。

入力

md
{{ 1 + 1 }}

出力

2

ディレクティブ

ディレクティブも動作します(設計上、生の HTML は Markdown でも有効です)。

入力

html
<span v-for="i in 3">{{ i }}</span>

出力

1 2 3 

<script><style>

Markdown ファイルのルート直下に置く <script><style> タグは、Vue の SFC と同様に動作します(<script setup><style module> などを含む)。大きな違いは <template> タグが無い点で、その他のルート直下のコンテンツは Markdown になることです。すべてのタグはフロントマターのに配置してください。

html
---
hello: world
---

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

## Markdown コンテンツ

現在の値: {{ count }}

<button :class="$style.button" @click="count++">Increment</button>

<style module>
.button {
  color: red;
  font-weight: bold;
}
</style>

Markdown での <style scoped> は避ける

Markdown で <style scoped> を使うと、そのページ内のすべての要素に特殊な属性を付与する必要があり、ページサイズが大きく膨らみます。ページ単位でローカルスコープが必要な場合は <style module> を推奨します。

VitePress のランタイム API(例:現在ページのメタデータにアクセスできる useData ヘルパー)も利用できます。

入力

html
<script setup>
import { useData } from 'vitepress'

const { page } = useData()
</script>

<pre>{{ page }}</pre>

出力

json
{
  "path": "/using-vue.html",
  "title": "Using Vue in Markdown",
  "frontmatter": {},
  ...
}

コンポーネントの利用

Markdown ファイルで、Vue コンポーネントを直接インポートして使用できます。

Markdown 内でのインポート

特定のページでしか使わないコンポーネントは、そのページで明示的にインポートするのがおすすめです。これにより適切にコード分割され、該当ページでのみ読み込まれます。

md
<script setup>
import CustomComponent from '../components/CustomComponent.vue'
</script>

# ドキュメント

これはカスタムコンポーネントを使う .md です

<CustomComponent />

## 続き

...

グローバル登録

ほとんどのページで使うコンポーネントは、Vue アプリインスタンスをカスタマイズしてグローバル登録できます。例は デフォルトテーマの拡張 を参照してください。

重要

カスタムコンポーネント名にはハイフンを含めるか、PascalCase を使用してください。そうでない場合、インライン要素として解釈されて <p> タグ内にラップされ、ブロック要素が入れられないためハイドレーション不整合を引き起こします。

見出し内でのコンポーネント利用

見出し内で Vue コンポーネントを使うこともできますが、次の書き方の違いに注意してください。

Markdown出力 HTML解析される見出し
 # text <Tag/> 
<h1>text <Tag/></h1>text
 # text `<Tag/>` 
<h1>text <code>&lt;Tag/&gt;</code></h1>text <Tag/>

<code> に包まれた HTML はそのまま表示されます。包まれていない HTML だけが Vue によってパースされます。

TIP

出力 HTML の生成は Markdown-it が担当し、見出しの解析は VitePress が担当します(サイドバーやドキュメントタイトルに利用)。

エスケープ

v-pre ディレクティブを付けた <span> などでラップすることで、Vue の補間をエスケープできます。

入力

md
This <span v-pre>{{ will be displayed as-is }}</span>

出力

This {{ will be displayed as-is }}

段落全体を v-pre のカスタムコンテナで囲む方法もあります。

md
::: v-pre
{{ This will be displayed as-is }}
:::

出力

{{ This will be displayed as-is }}

コードブロック内でのアンエスケープ

既定では、フェンス付きコードブロックは自動で v-pre が付与され、Vue の構文は処理されません。フェンス内で Vue 風の補間を有効にするには、言語に -vue サフィックスを付けます(例:js-vue)。

入力

md
```js-vue
Hello {{ 1 + 1 }}
```

出力

js
Hello 2

この方法では、一部のトークンが正しくシンタックスハイライトされない場合があります。

CSS プリプロセッサの利用

VitePress は CSS プリプロセッサ(.scss.sass.less.styl.stylus)を標準サポートしています。Vite 固有のプラグインは不要ですが、各プリプロセッサ本体のインストールは必要です。

# .scss / .sass
npm install -D sass

# .less
npm install -D less

# .styl / .stylus
npm install -D stylus

その後、Markdown やテーマコンポーネントで次のように使えます。

vue
<style lang="sass">
.title
  font-size: 20px
</style>

Teleport の利用

現時点で VitePress は、SSG における Teleport を body へのみサポートしています。その他のターゲットへ Teleport したい場合は、組み込みの <ClientOnly> でラップするか、postRender フックで最終ページ HTML の適切な位置に Teleport のマークアップを注入してください。

Details
vue
<script setup lang="ts">
import { ref } from 'vue'
const showModal = ref(false)
</script>

<template>
  <button class="modal-button" @click="showModal = true">Show Modal</button>

  <Teleport to="body">
    <Transition name="modal">
      <div v-show="showModal" class="modal-mask">
        <div class="modal-container">
          <p>Hello from the modal!</p>
          <div class="model-footer">
            <button class="modal-button" @click="showModal = false">
              Close
            </button>
          </div>
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
.modal-mask {
  position: fixed;
  z-index: 200;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.3s ease;
}

.modal-container {
  width: 300px;
  margin: auto;
  padding: 20px 30px;
  background-color: var(--vp-c-bg);
  border-radius: 2px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
  transition: all 0.3s ease;
}

.model-footer {
  margin-top: 8px;
  text-align: right;
}

.modal-button {
  padding: 4px 8px;
  border-radius: 4px;
  border-color: var(--vp-button-alt-border);
  color: var(--vp-button-alt-text);
  background-color: var(--vp-button-alt-bg);
}

.modal-button:hover {
  border-color: var(--vp-button-alt-hover-border);
  color: var(--vp-button-alt-hover-text);
  background-color: var(--vp-button-alt-hover-bg);
}

.modal-enter-from,
.modal-leave-to {
  opacity: 0;
}

.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
  transform: scale(1.1);
}
</style>
md
<ClientOnly>
  <Teleport to="#modal">
    <div>
      // ...
    </div>
  </Teleport>
</ClientOnly>

VS Code の IntelliSense サポート

Vue は Vue - Official VS Code plugin により、標準で IntelliSense を提供します。ただし .md ファイルでも有効にするには、設定ファイルをいくつか調整する必要があります。

  1. tsconfig/jsconfig の includevueCompilerOptions.vitePressExtensions.md パターンを追加します。
json
{
  "include": [
    "docs/**/*.ts",
    "docs/**/*.vue",
    "docs/**/*.md",
  ],
  "vueCompilerOptions": {
    "vitePressExtensions": [".md"],
  },
}
  1. VS Code の設定で、vue.server.includeLanguagesmarkdown を追加します。
json
{
  "vue.server.includeLanguages": ["vue", "markdown"]
}

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