React Router などを使った SPA (Single Page Application) な Web サイトは、GitHub Pages でそのまま動作させようとしてもうまく動きません。 ここでは、その理由と、対応方法について説明します。
GitHub Pages で SPA サイトが動作しない理由
たとえば、React Router を使った Web サイトは、次のような URL を使って目的のコンテンツ (/book/123
) を表示するようルーティングします。
React Router を使ったアプリで、この URL を実際に処理するファイルは、
であり、そこに記述された JavaScript ファイル内で、URL の末尾の /book/123
という部分をルーティング用のパス文字列として処理します。
つまり、前述の URL の /book/123
という部分は、単なるアプリ用のデータであり、実際に存在するファイルを示しているわけではありません。
一方、GitHub Pages はそのような事情を知らないので、/book/123
という URL でアクセスしようとすると、/book/123/index.html
というファイルを見つけようとして 404 エラー になってしまいます。
先頭ページ (/index.html
) を開いた後で、JavaScript でルーティング(React の Link
コンポーネントによる遷移)を行っている間はうまく動作するのですが、ページをリロードしたり、Web ブラウザのアドレスバーに URL を直接入力したりすると、やはり 404 エラーになってしまいます。
OAuth 認証を使用した Web サイトの場合は、リダイレクトによって指定された URL へのアクセスが必要になるため、これは大きな制約になります。
解決方法
ここでは、こちら で紹介されている方法を参考にして、カスタム 404 ページを利用したハックで React Router をうまく動作させるようにしてみます。
上記サイトのコードは若干複雑なので、もう少し直感的なコードで同様のことを行えるようにします。
具体的には、クエリ文字列を自力で分解/再構築しているところを、encodeURIComponent
/decodeURIComponent
でサクッと終わらせちゃいます。
仕組み
この 404 ページハックは、次のような仕組みで GitHub Pages 上で SPA アプリ (React Router) を動作可能にします。
https://yourname.github.io/repo-name/book/123
といった URL でアクセスすると、カスタム404.html
ページが呼び出される。404.html
の中の JavaScript で、https://yourname.github.io/repo-name
にリダイレクトする。このとき、URL から/book/123
というパラメータを抽出し、?q=book%2F123
のように URL エンコードしたクエリパラメータとして付加する。- Web サイトトップの
index.html
が呼び出されるので、JavaScript を使って URL 末尾のクエリ文字列 (?q=book%2F123
を取り出し、もとのパラメータの形 (/book/123
) に戻す。 window.history.replaceState()
で Web ブラウザのアドレスを/book/123
にセットし、React Router を動作させる(このときサーバーアクセスは発生しません)。
実装
404.html のアップロード
GitHub Pages として公開するリポジトリのルートに、次のような 404.html
ファイルをアップロードします。
https://yourname.github.io/
といった URL ではなく、https://yourname.github.io/project名/
といった、1階層下の URL で公開するページの場合は、上記の SEGMENT_COUNT
の値を 0
から 1
に変更しておく必要があります(リダイレクト先の URL が調整されます)。
(コラム)バンドルツールで 404.html を追加する
Parcel などのバンドルツールで Web サイトのビルドを行っている場合は、エントリポイントとなるページとして 404.html
を追加します。
例えば、parcel
コマンドを使用している場合は、次のように指定すると build/404.html
ファイルが出力されます。
build/404.html
の内容は基本的に src/404.html
のコピーですが、記述内容が圧縮されたファイルになっています。
index.html に JavaScript を追加
プロジェクトルートの index.html
の先頭の方に、次のような JavaScript コードを追加します。
このコードは、React などの JavaScript コードよりも先に実行する必要があります(Router コンポーネントがパスを処理する前に実行する)。
以上のように、404.html
を配置し、index.html
に JavaScript コードを追加するだけで、GitHub Pages 上で SPA アプリケーションが動作するようになります。
React Router のベース URL の調整(必要に応じて)
React Router は、デフォルトで URL のドメインを除いた残りの部分 (例: /repo-name/books/123
)をルーティング用のパスとして使用します。
Web サイトのルート階層に index.html
を配置しない場合は、basename
プロパティでそのディレクトリ名を知らせておく必要があります。
次の例では、basename
として、開発時はデフォルトの /
を、実運用時は /repo-name
を使用するように設定しています。
この設定により、/main
というパスは、実際には /repo-name/main
としてハンドルされるようになります。