はじめに
SPA や SSG のビルド時のアウトプットには違いがあります。ファイル構造が異なるために、ウェブサーバーへ設定をしないと、リロード時に 404 が出たりすることもあります。 この記事では、SPA と SSG の違いについて、デプロイという観点から見ていきます。そして実際に デプロイするときに、どのような観点に注意が必要か説明します。
説明のための SPA、SSG 環境 としては、Vite を用いた Vue3 環境を用います。 基本的にはフレームワークごとに、アウトプットのファイル構造や、ルーティングに大きな違いはないと思うので、他のフレームワークでも参考になると思います。
余談ですが、Vite は ESbuild を用いた高速な開発環境を提供してくれます。
Vue
だけでなく、React
やPreact
、最近ではlit-element
も公式にサポートされ、多くのフロントエンドフレームワークの開発基盤になるのもそう遠くないかもしれません。
この機会にぜひ使ってみてください。🚀
環境構築
どちらともなるべく最小構成で、ルーターの設定や動的パスの設定のみを行います。
Vue3 SPA
次のようなページを用意します。
/
/hello
/hello/world
/hello/[:name]
ルーティングテーブルは後述する SSG と同じにします。
この状態でビルドすると次のようなファイル構造になります。
SPA なのでindex.html
と assets の中にページごとにチャンクされたjs
ファイルが生成されています。ちなみにページ単位のチャンク分割は、ルーティングの設定の際、dynamic import でコンポーネントを設定すると適応されます。
続いてデプロイを行います。デプロイ先は Firebase Hosting にします。 幸い、Firebase の CLI で設定ファイルを生成するときに、SPA かどうか聞いてくれるので、いわれるがままの設定にします。
publish
オプションをビルドのアウトプットディレクトリを同じにする必要があります。
用意したページは、この設定ですべてうまく動作します。動的ルートだろうがネストされたルートだろうが、hosting のrewrites
のすべてのパターンを/index.html
にリダイレクトしているからです。
ただしこれによって、用意したページ以外のアクセスであっても、404 などを返さなくなっています。
SPA の場合はアプリケーションでルーティングを判断するため、アプリケーション側で、404 などのページを用意する必要があります。 先程のルーティングテーブルに 404 ページを登録し、適当な 404 コンポーネントを作ります。
Vue の場合は routes のパスマッチングは先頭から行われるため、404 のようなその他すべてに一致させるためには、ルーティングテーブルの一番最後に 404 用のコンポーネントを追加します。
ちなみに Vue Router の Catch All パターンマッチングは 4 系と3系では異なるため、注意が必要です。
- Vue Router 4(next):
path: '/:pathMatch(.*)*'
- Vue Router 3:
path: '/*'
これでデプロイをし直すと、定義していないルーティングテーブルへのアクセスで 404 ページを返すことができます。 ここまでをまとめると、
- SPA では html ファイルがひとつなため、ウェブサーバーは
index.html
をリダイレクトする必要がある。 - どのパスのアクセスでも
index.html
が返ってしまうので、アプリケーション側で 404 などの設定が必要。
Vue3 SSG
続いて SSG の場合を見てみましょう。
以下のようにビルドコマンドを変更します。
ルーティングテーブルは SPA と同じとします。
この状態でビルドすると、次のような出力を得られます。
ちゃんとそれぞれの html ファイルが出力されていますね。動的ルーティングは、ビルド時にパラメータを渡してあげなければ、静的ファイルは生成されません。 Firebase Hosting の設定は、ひとまずデフォルトである次のようにしておきます。
この状態でデプロイすると、/
へのアクセスはうまくいきます。しかし、その他のパスへのアクセスは 、次のようなレスポンスを返してきます。
この結果は少し興味深いので見ていきます。/
への結果は当然として、/hello
や/hello/world
は HTTP レスポンスとして 404 が返ります。
ホスティングの設定としては、html ファイルへのリダイレクトをしていないので、ファイルが見つからなかったということですね。
/hello/world.html
への結果は、動的パスを指定した:name.vue が返っています。/hello/:id
として動的パスを設定したので、ここにマッチしたわけですね。
/hello.html
への結果は、Catch All で定義した 404.vue が返っています。これは、html ファイルはヒットしたが、/hello.html
というパスがルーティングテーブルになかったために、自己定義の 404 ページが表示されたということです。
実際上の例の.html
を加えたパスは、レスポンスこそ正しいですが、正しく動作しません。.html
を加えたパスをルーティングテーブルには定義していないため、JavaScript が正しく認識されないためです。
SSG を正しく動作させるためには、ホスティング側の設定としては次のような対応が必要です。
とてもシンプルですね✨。このパターンリダイレクトは多くの Web サーバには簡単に設定できるので Firebase Hosting の場合を見てみましょう。
cleanUrls
属性は、URL に .html 拡張子を含めるかどうかを制御できます。
trailingSlash
属性は、静的コンテンツの URL に末尾のスラッシュを含めるかどうかを制御できます。これによって、/hello/
のようなリクエストも正しく捌くことができます。
まとめ
SPA、SSG の仕組みやホスティングについて簡単に見てきました。上の例は、振り返ると至極当たり前のことだったりするのですが、SPA や SSG を始めたばかりだと、そもそもどのように動いているのかわからなかったりします。最後に簡単に要点だけまとめます。
- SPA、SSG とともにまずは 実際の html ファイルを返すようにリダイレクトが必要。
- ルーターは URL パスで JavaScript を制御するため、定義したルーティングパスが目的の html ファイルにリダイレクトするように、Web サーバーの設定が必要。
Edit this page on GitHub