MarkdownでVueを使う
VitePress では、各 Markdown ファイルはまず HTML にコンパイルされ、その後 Vue の単一ファイルコンポーネント(SFC) として処理されます。つまり、Markdown 内で Vue のあらゆる機能が使えます。動的テンプレート、Vue コンポーネントの利用、<script>
タグを追加してページ内ロジックを書くことも可能です。
なお、VitePress は Vue のコンパイラを利用して、Markdown コンテンツの純粋に静的な部分を自動検出・最適化します。静的コンテンツは単一のプレースホルダノードに最適化され、初回訪問時の JavaScript ペイロードから除外されます。クライアント側のハイドレーションでもスキップされます。要するに、そのページで動的な部分に対してだけコストを支払うことになります。
SSR 互換性
Vue の使用は SSR 互換である必要があります。詳細と一般的な回避策は SSR 互換性 を参照してください。
テンプレート記法
補間
各 Markdown は最初に HTML にコンパイルされ、その後 Vite の処理パイプラインで Vue コンポーネントとして扱われます。つまり、テキスト内で Vue 風の補間が使えます。
入力
{{ 1 + 1 }}
出力
2
ディレクティブ
ディレクティブも動作します(設計上、生の HTML は Markdown でも有効です)。
入力
<span v-for="i in 3">{{ i }}</span>
出力
1 2 3
<script>
と <style>
Markdown ファイルのルート直下に置く <script>
と <style>
タグは、Vue の SFC と同様に動作します(<script setup>
や <style module>
などを含む)。大きな違いは <template>
タグが無い点で、その他のルート直下のコンテンツは Markdown になることです。すべてのタグはフロントマターの後に配置してください。
---
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
ヘルパー)も利用できます。
入力
<script setup>
import { useData } from 'vitepress'
const { page } = useData()
</script>
<pre>{{ page }}</pre>
出力
{
"path": "/using-vue.html",
"title": "Using Vue in Markdown",
"frontmatter": {},
...
}
コンポーネントの利用
Markdown ファイルで、Vue コンポーネントを直接インポートして使用できます。
Markdown 内でのインポート
特定のページでしか使わないコンポーネントは、そのページで明示的にインポートするのがおすすめです。これにより適切にコード分割され、該当ページでのみ読み込まれます。
<script setup>
import CustomComponent from '../components/CustomComponent.vue'
</script>
# ドキュメント
これはカスタムコンポーネントを使う .md です
<CustomComponent />
## 続き
...
グローバル登録
ほとんどのページで使うコンポーネントは、Vue アプリインスタンスをカスタマイズしてグローバル登録できます。例は デフォルトテーマの拡張 を参照してください。
重要
カスタムコンポーネント名にはハイフンを含めるか、PascalCase を使用してください。そうでない場合、インライン要素として解釈されて <p>
タグ内にラップされ、ブロック要素が入れられないためハイドレーション不整合を引き起こします。
見出し内でのコンポーネント利用 ⚡
見出し内で Vue コンポーネントを使うこともできますが、次の書き方の違いに注意してください。
Markdown | 出力 HTML | 解析される見出し |
---|---|---|
| <h1>text <Tag/></h1> | text |
| <h1>text <code><Tag/></code></h1> | text <Tag/> |
<code>
に包まれた HTML はそのまま表示されます。包まれていない HTML だけが Vue によってパースされます。
TIP
出力 HTML の生成は Markdown-it が担当し、見出しの解析は VitePress が担当します(サイドバーやドキュメントタイトルに利用)。
エスケープ
v-pre
ディレクティブを付けた <span>
などでラップすることで、Vue の補間をエスケープできます。
入力
This <span v-pre>{{ will be displayed as-is }}</span>
出力
This {{ will be displayed as-is }}
段落全体を v-pre
のカスタムコンテナで囲む方法もあります。
::: v-pre
{{ This will be displayed as-is }}
:::
出力
{{ This will be displayed as-is }}
コードブロック内でのアンエスケープ
既定では、フェンス付きコードブロックは自動で v-pre
が付与され、Vue の構文は処理されません。フェンス内で Vue 風の補間を有効にするには、言語に -vue
サフィックスを付けます(例:js-vue
)。
入力
```js-vue
Hello {{ 1 + 1 }}
```
出力
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 やテーマコンポーネントで次のように使えます。
<style lang="sass">
.title
font-size: 20px
</style>
Teleport の利用
現時点で VitePress は、SSG における Teleport を body へのみサポートしています。その他のターゲットへ Teleport したい場合は、組み込みの <ClientOnly>
でラップするか、postRender
フックで最終ページ HTML の適切な位置に Teleport のマークアップを注入してください。
Details
<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>
<ClientOnly>
<Teleport to="#modal">
<div>
// ...
</div>
</Teleport>
</ClientOnly>
VS Code の IntelliSense サポート
Vue は Vue - Official VS Code plugin により、標準で IntelliSense を提供します。ただし .md
ファイルでも有効にするには、設定ファイルをいくつか調整する必要があります。
- tsconfig/jsconfig の
include
とvueCompilerOptions.vitePressExtensions
に.md
パターンを追加します。
{
"include": [
"docs/**/*.ts",
"docs/**/*.vue",
"docs/**/*.md",
],
"vueCompilerOptions": {
"vitePressExtensions": [".md"],
},
}
- VS Code の設定で、
vue.server.includeLanguages
にmarkdown
を追加します。
{
"vue.server.includeLanguages": ["vue", "markdown"]
}