Draw.ioファイルの動的埋め込み調査レポート
調査目的
Draw.ioファイル(.drawio)をVueコンポーネント内で動的に読み込み、ページをリロードするだけで最新の図が反映される仕組みを実現する。
調査結果サマリー
結論:成功 ✓
viewer-static.min.jsを使用することで、Draw.ioファイルを動的に読み込んで表示できることを確認した。
成功した方法
- 使用ライブラリ:
https://viewer.diagrams.net/js/viewer-static.min.js - 実装方法: data-mxgraph属性を持つdiv要素にXMLデータをJSONで渡す
- CORS問題: 解決済み(ローカルファイルを直接読み込むため、CORSエラーなし)
調査プロセス
試行1: iframe + URL指定(失敗)
方法:
viewerUrl.value = `https://viewer.diagrams.net/?url=${encodeURIComponent(fileUrl)}`
結果: ❌ 失敗
- Draw.ioビューアーはlocalhostのURLを読み込めない(CORS制限)
- エラー: "ファイルが見つかりません"
試行2: iframe + Base64エンコード(失敗)
方法:
const base64 = btoa(unescape(encodeURIComponent(text)))
viewerUrl.value = `https://viewer.diagrams.net/#R${base64}`
結果: ❌ 失敗
- エラー: "invalid code lengths set"
- Draw.ioビューアーが期待する圧縮形式と不一致
試行3: viewer-static.min.js(成功)
方法:
<template>
<div
class="mxgraph"
:data-mxgraph="diagramData"
></div>
</template>
<script setup>
const diagramData = ref('')
async function loadDiagram() {
const response = await fetch('/2025-11-18/freee-ai-system.drawio')
const xml = await response.text()
diagramData.value = JSON.stringify({
highlight: '#0000ff',
nav: true,
resize: true,
toolbar: 'zoom layers tags lightbox',
edit: '_blank',
xml: xml
})
loadViewerScript()
}
function loadViewerScript() {
const script = document.createElement('script')
script.src = 'https://viewer.diagrams.net/js/viewer-static.min.js'
script.async = true
document.body.appendChild(script)
}
</script>
結果: ✅ 成功
- Draw.ioファイルが正常に表示される
- インタラクティブな操作(ズーム、レイヤー表示など)が可能
- CORSエラーなし
実装の詳細
前提条件
Nuxtサーバーミドルウェアで.drawioファイルを提供できるように設定:
// server/middleware/content-images.ts
const match = url.match(/^\/(\d{4}-\d{2}-\d{2})\/([^/]+\.(png|jpg|jpeg|gif|webp|svg|drawio))$/i)
const mimeTypes: Record<string, string> = {
// ...
drawio: 'application/xml'
}
データフロー
data-mxgraph属性の構造
{
"highlight": "#0000ff", // ハイライト色
"nav": true, // ナビゲーション表示
"resize": true, // リサイズ可能
"toolbar": "zoom layers tags lightbox", // ツールバー機能
"edit": "_blank", // 編集ボタンで新しいタブで開く
"xml": "<mxfile>...</mxfile>" // Draw.io XML データ
}
メリット・デメリット
メリット
✅ CORSエラーなし: ローカルファイルを直接読み込むため、localhostでも動作
✅ iframeなし: ページに直接埋め込まれるため、スタイリングが容易
✅ 公式ライブラリ: Draw.io公式のviewer-static.min.jsを使用
✅ インタラクティブ: ズーム、レイヤー表示などの操作が可能
✅ 動的更新: ファイルを編集してページをリロードするだけで反映
✅ Gitで管理: .drawioファイルはテキストベースのXMLなので、バージョン管理が容易
デメリット
⚠️ 外部依存: viewer.diagrams.netのCDNに依存
⚠️ 初回ロード時間: JavaScriptライブラリのロードに若干時間がかかる
⚠️ ビルド時非レンダリング: SSR/SSGでは描画されない(クライアントサイドのみ)
代替案の検討
代替案1: SVG自動変換
概要: Draw.ioファイルの変更を監視して、自動的にSVGに変換するスクリプトを作成
メリット:
- SSR/SSGで描画可能
- CDN依存なし
デメリット:
- ビルドプロセスが複雑化
- インタラクティブ性なし
- 変換スクリプトの保守が必要
代替案2: Nuxtサーバーサイドプロキシ
概要: Nuxtサーバー経由でDraw.ioビューアーにファイルを提供
メリット:
- CORS問題を回避可能
- iframe方式でも動作
デメリット:
- サーバーサイドの実装が必要
- プロキシ経由の通信がオーバーヘッド
推奨実装
現時点ではviewer-static.min.js方式を推奨する。
理由:
- 実装がシンプル
- CORSエラーなし
- インタラクティブ性あり
- 公式ライブラリで安定
実装例
実際の動作例は以下で確認できます:
- デモページ:
/blog/drawio-viewer-test - ソースコード:
apps/web/app/pages/blog/drawio-viewer-test.vue - Draw.ioファイル:
apps/web/content/2025-11-18/freee-ai-system.drawio
課題と解決
キャッシュ問題による動的更新の遅延
問題: 初期実装では、.drawioファイルを編集してページをリロードしても、変更が即座に反映されなかった。サーバーを再起動する必要があった。
原因:
- サーバーミドルウェア(
server/middleware/content-images.ts)が全てのファイルにcache-control: public, max-age=31536000(1年間のキャッシュ)を設定 - ブラウザのfetchがキャッシュを使用
解決策:
1. サーバーミドルウェアでの開発環境用キャッシュ制御
// server/middleware/content-images.ts
const isDev = process.env.NODE_ENV === 'development'
const cacheControl = (ext === 'drawio' && isDev)
? 'no-cache, no-store, must-revalidate' // 開発環境では即座に反映
: 'public, max-age=31536000' // 本番環境では長期キャッシュ
2. Vueコンポーネントでのキャッシュバスティング
// pages/blog/drawio-viewer-test.vue
const response = await fetch(`/2025-11-18/freee-ai-system.drawio?t=${Date.now()}`, {
cache: 'no-store'
})
3. サーバーミドルウェアでクエリパラメータ対応
// server/middleware/content-images.ts
const pathname = url.split('?')[0] // クエリパラメータを除去
const match = pathname.match(/^\/(\d{4}-\d{2}-\d{2})\/([^/]+\.(png|jpg|jpeg|gif|webp|svg|drawio))$/i)
結果: ✅ 解決済み
- .drawioファイルを編集
- ページをリロード(サーバー再起動不要)
- 変更が即座に反映される
開発環境で真の「動的埋め込み」が実現した。
本番環境での「Not a diagram file」エラー
問題: Cloudflare Pagesにデプロイすると「Not a diagram file」エラーが発生
原因:
nuxt.config.tsのNitroフックで、画像ファイル(png, jpg等)のみをpublic/ディレクトリにコピーしており、.drawioファイルが本番ビルドに含まれていなかった
解決策:
// nuxt.config.ts
} else if (/\.(png|jpg|jpeg|gif|webp|svg|drawio)$/i.test(entry.name)) {
// .drawioを正規表現に追加
結果: ✅ 解決済み
- ビルド時に.drawioファイルもpublicディレクトリにコピー
- 本番環境でも正常に表示
今後の課題
1. オフライン対応
CDNに依存しているため、オフライン環境では動作しない。
解決策:
viewer-static.min.jsをローカルにホストpublic/js/viewer-static.min.jsに配置して参照
2. SSR/SSG対応
現在はクライアントサイドのみで描画される。
解決策:
- サーバーサイドでSVGに変換してレンダリング
- または、静的ビルド時にSVGを生成
3. パフォーマンス最適化
大きな図ではロード時間がかかる可能性がある。
解決策:
- 遅延ロード(Intersection Observer)
- 複数の図がある場合は、1つのviewer-static.min.jsを共有
参考資料
- Draw.io Embed Mode Documentation
- Draw.io Embedding Walk-through
- viewer-static.min.js
- GitHub Discussion: Viewing a .drawio file in an HTML document
まとめ
Draw.ioファイルをVueコンポーネント内で動的に読み込む方法として、viewer-static.min.jsを使用する実装が最も実用的であることが確認できた。
この方法により:
- ✅ Draw.ioファイルを編集したら、ページをリロードするだけで反映
- ✅ CORSエラーなしでlocalhostでも動作
- ✅ SVG/PNGへのエクスポート不要
- ✅ Gitで履歴管理可能
動的埋め込みは可能であり、実用的な実装方法が確立された。