SPAの白画面問題の正体とJSバンドル

SPAで発生する「白画面問題」の正体

SPA(Single Page Application)を運用していると、一定確率で「画面が真っ白になって何も表示されない」という報告が来ます。しかも再現が難しく、開発環境では問題が起きないことも多いです。サーバは正常、APIも応答しているのにユーザには何も見えない。この現象は回線障害ではなく、JavaScriptバンドルとブラウザ実行順序に起因します。

結論から言うと、SPAの白画面問題は「描画失敗」ではありません。アプリケーションが起動していない状態です。HTMLは取得されていても、JavaScriptの読み込み・解析・実行のどこかで止まると、DOMが構築されず画面は空のままになります。

SPAの画面はHTMLでは作られていない

典型的なSPAのHTMLは次のような形です。

<body>
  <div id="app"></div>
  <script src="/assets/app.bundle.js"></script>
</body>

ここにはコンテンツが存在しません。ブラウザがHTMLを表示しても、ユーザが見るべき情報はありません。すべてのUIはJavaScriptによって生成されます。

つまりSPAでは、

HTML表示 = 画面表示

ではありません。

JavaScript実行 = 画面表示

です。この構造が白画面の原因になります。

JavaScriptバンドルとは何か

現代のフロントエンドは、多数のモジュールをまとめた単一のファイルを配信します。これがJavaScriptバンドルです。ReactやVue、状態管理ライブラリ、ルータ、UIコンポーネントなどが1つのファイルに結合されています。

app.bundle.js
  ├ framework
  ├ router
  ├ store
  ├ components
  └ business logic

ブラウザはこの巨大なスクリプトを次の順番で処理します。

1. ダウンロード
2. 構文解析(Parse)
3. 実行(Evaluate)

このどこかで問題が起きると、アプリケーションは起動しません。

白画面が発生する具体的なポイント

1. ダウンロード失敗**

CDNキャッシュ不整合やネットワーク遮断によりbundle.jsが取得できない場合、ブラウザはdiv要素だけを持つHTMLを表示します。つまり白画面になります。

2. 構文エラー**

const user = await getUser();

トランスパイル設定の不備や古いブラウザでは、これだけで実行が停止します。JavaScriptは1つのエラーでスクリプト全体が止まります。

3. 実行時例外**

render(user.profile.name);

APIレスポンスの形式変更などでundefinedアクセスが起きると、初期描画前にクラッシュします。

なぜ開発環境では再現しないのか

開発環境では以下の条件が揃っています。

  • 高性能PC
  • 最新ブラウザ
  • キャッシュがない
  • 高速回線

しかし本番ユーザは違います。

  • 古いAndroid端末
  • 企業プロキシ
  • 低速CPU
  • 途中切断される通信

特に起きやすいのが「途中までダウンロードされたJS」です。ファイル破損が起きると構文解析エラーになります。サーバログには残りません。

SSRとの決定的な違い

SSRではHTMLに内容があります。

<h1>商品名</h1>
<p>説明...</p>

JavaScriptが失敗しても、最低限の情報は表示されます。これをProgressive Enhancementと言います。SPAではこの退避経路が存在しません。

つまり白画面はバグではなく、SPAの設計特性です。

JavaScriptバンドルと白画面の関係

バンドルが大きくなるほど、白画面の確率は上がります。理由は単純で、処理時間と失敗確率が増えるからです。

  • ダウンロード時間が伸びる
  • パース時間が増える
  • 実行時間が増える
  • メモリ使用量が増える

スマートフォンでは、JS解析に数秒かかることもあります。その間、画面は何も表示されません。

よくある誤解

ローディングスピナーを出せば解決すると思われがちですが、スピナーはJavaScriptで描画されます。つまりJSが起動していない状態では表示できません。

また「APIが遅いから白画面」と判断されることも多いですが、API到達前に停止しているケースが多数です。

実務で取るべき対策

白画面対策は性能改善ではなく故障前提設計です。

  • エラーバウンダリの設置
  • コード分割(route-based splitting)
  • 最小JSブートストラップ
  • 互換性トランスパイル
  • scriptのdefer指定
<script defer src="/app.bundle.js"></script>

さらに重要なのは、最低限のHTMLを返すフォールバックです。ログイン画面やメンテナンス情報をプレーンHTMLで返すだけでも、完全な白画面を避けられます。

運用上の注意

SPAではサーバ監視だけでは障害検知できません。ブラウザ側のエラー収集(Sentryなど)が必須になります。実際の障害の多くはサーバではなくクライアントで発生します。

最後に

SPAの白画面問題は実装ミスではなく、アプリケーションをブラウザに配置した結果です。ネイティブアプリが起動失敗するのと同じ性質を持っています。

SPAを採用するなら、「常に正しく起動する」前提ではなく、「起動に失敗することがある」前提で設計する必要があります。表示速度の最適化よりも、失敗時に何を見せるかを決めることが、白画面を減らす最も現実的な対策になります。