[Astro #49] IndexedDBを活用した3Dアセットの完全キャッシュ化とバージョニング管理
はじめに
WebXR環境(React Three Fiber + Astro)において、VRMモデルやGLBステージ、巨大なWebPスカイボックスなどの読み込み時間は、ユーザー体験を損なう最大のボトルネックでした。
今回は、ブラウザのローカルストレージである「IndexedDB」を活用し、重い3Dアセットを完全キャッシュ化。
さらに、更新漏れ(古いキャッシュが残り続ける問題)を防ぐための「JSONベースのバージョニング戦略」を実装し、リロードしても即座に世界が復元される爆速の「Wired」環境を構築しました。
1. キャッシュ&バージョニングの基本戦略
アセット(バイナリデータ)を IndexedDB に保存する際、最も厄介なのが「ファイルの中身を更新したのに、ブラウザが古いキャッシュを読み込み続けてしまう」という問題です。
これを解決するため、以下のような役割分担を行いました。
- JSONファイル(サーバー側) : アセットのパスと「バージョン情報(
version)」を記載した司令塔。これは容量が数KBと軽いため、常に最新のものをネットワークから取得する。 - IndexedDB(ブラウザ側) :
Dexie.jsを使用し、重いバイナリデータ(Blob)と、その時点のバージョン情報をセットで保管する。
【判定ロジック】
- サーバーから取得したJSONの
versionと、IndexedDB内のversionを比較する。 - 一致すれば(
CACHE_HIT) :ネットワーク通信を行わず、DBから爆速でBlob URLを生成して読み込む。 - 不一致なら(
DOWNLOAD) :新しくfetchしてバイナリを取得し、DBを新しいデータとバージョンで上書き保存する。
これにより、JSONのバージョン文字列を書き換えるだけで、世界中のユーザーのキャッシュを自動的にパージ・更新できる堅牢なマスターキーが完成しました。
2. 実装のコア:db.ts と assetCache.ts
IndexedDBの複雑な操作を隠蔽し、使いやすくするために Dexie.js を導入しました。
データベースの定義 (src/lib/db.ts)
モデル、アニメーション、ステージ、スカイボックスごとにテーブル(Store)を分割しました。
注意点:Vite環境でビルドエラーを防ぐため、Table は必ず import type で読み込むこと。
キャッシュ取得ユーティリティ (src/utils/assetCache.ts)
任意のテーブルとJSONエントリーを渡し、URLを解決する共通関数 getCachedAssetUrl を作成。
内部では、先述の「バージョン比較→ヒットならBlob URL返却、ミスならDLしてDB更新」という処理を一手に引き受けています。
3. 各コンポーネントへの適用
準備したユーティリティを、React Three Fiber の各コンポーネントに組み込みました。
アバター(VRM)とステージ(GLB)
WiredAvatar や WiredStage では、Reactの useEffect を使って非同期でBlob URLを解決。URLが確定してから useGLTF や useWiredVRM フックに渡すように処理を分離しました。
また、スカイボックスコンポーネントへの Props 渡し間違い(texturePath ではなく data を渡す)による画面暗転バグも修正し、正常なキャッシュ動作を確認しました。
アニメーション(VRMA)のシームレス化
useWiredAnimation.ts 内の loadAndCrossfade 関数を非同期(async)化。
ローダーがアニメーションを読み込む直前に getCachedAssetUrl を経由させることで、状態(Phase)が切り替わって新しいモーションが必要になるたびに発生していた「読み込みのラグ」が完全に消滅しました。キャラクターに真の生命感が宿った瞬間です。
4. 開発中に遭遇したエラーと対処法
実装を進める中で、Reactのステート更新が活発になったことで顕在化したエラーがありました。
-
Manifest 404エラーの連発
-
原因:アニメーションの切り替え等でステートが更新され、DOMツリーが再評価されるたびに、存在しない
manifest.webmanifestをブラウザが探しに行っていた。 -
解決策:
publicフォルダに最小限の有効なJSONを記述したmanifest.webmanifestを配置し、ブラウザに正常にキャッシュさせることで沈静化。 -
Astro Dev Toolbar の 504 Gateway Timeout
-
原因:重いアセットのロードや頻繁なリロードにより、Astroの開発用サーバーが処理落ちし、Dev Toolbar用のスクリプト返却がタイムアウトしていた。
-
解決策:VR開発においてはDOM監視用のDev Toolbarは不要と判断し、
astro.config.mjsにてdevToolbar: { enabled: false }を設定して完全無効化。コンソールが非常にクリーンになった。
まとめ
「とりあえず動く」状態から脱却し、DB設計という強固な基盤を敷いたことで、今後の拡張性が飛躍的に向上しました。 リロードのたびに数秒〜数十秒待たされていた状態から、瞬時に空間とアバターが立ち上がる「真のWired」体験へ。探索的プロトタイピングのフェーズを経て、ついに本番環境に耐えうる舞台装置が完成しました。