🚀

ViteのbaseとReact Routerのbasename問題

に公開

はじめに

以下の設定でReactアプリケーションをViteでビルドし、GitHub Pagesにアップロードした際、ページを再読み込みすると404エラーが発生する問題に直面しました。

vite.config.js

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  base: '/blogApp/',
  plugins: [react()],
})

App.jsx

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import HomePage from './Components/HomePage';
import Login from './Components/Login';

const App = () => {
  return (
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<HomePage />} />
      <Route path="/login" element={<Login />} />
    </Routes>
  </BrowserRouter>
  )
}

export default App

ローカル環境では問題なく動作していたにもかかわらず、GitHub Pagesにデプロイすると、URLバーに直接パス(例:/about)を入力したり、ページをリロードしたりした場合に、Webサイトが見つからないというエラー画面が表示されました。

これは、ViteとReact Routerの設定の間に生じる不一致が原因でした。

一行要約

GitHub Pagesのような静的ホスティングサービスでは、HashRouterを使用するのが最もシンプルで一般的な解決策です。

詳説

React.js と index.html

Routerについて詳しく説明する前にReactの仕組みについておさらいしましょう。
ブラウザがWebサイトを表示するとき、一番最初に読み込むのはindex.htmlです。
このファイルは、ウェブページの「骨組み」であり、Reactにおいては、アプリケーションを動かすための「スイッチ」でもあります。

index.htmlには、通常次のような要素が含まれています。

<div id="root"></div>  
<script src="/path/to/your/bundle.js"></script>
  • <div id="root"></div>: Reactがコンポーネントを配置する場所です
  • <script>: ここでReactのコードを読み込みます

ブラウザはまずindex.htmlを読み込み、次にJavaScriptファイルを読み込んで、id="root" の中にすべてのUIを動的に生成します。

ページ遷移を管理する

Reactアプリケーションは、通常複数のページが1つのHTMLファイルで構成される「シングルページアプリケーション(SPA)」です。
SPAではページを切り替えるとき、ブラウザがサーバーに新しいHTMLをリクエストするのではなく、JavaScriptがDOMを操作して表示内容を切り替えます。

この「表示内容の切り替え」をURLに合わせて行うのがReact Routerです。

React Routerとは

React Routerは、ブラウザのURLを監視し、そのURLに応じて画面に表示するコンポーネントを切り替えるためのライブラリです。
これにより、Webサイトのページ遷移のような動作を、フルリロードなしで実現できます。

React Router には、主にBrowserRouterHashRouterという2つのルーターが存在し、それぞれ異なる方法でURLを扱います。

BrowserRouter

  • URLの例: https://example.com/about
  • 特徴:
    • クリーンなURL で、見た目がきれいです。
    • サーバー側の設定が必要です。 /about のようなパスに直接アクセスしたときに、サーバーが index.html を返すように設定しないと、404エラーになってしまいます。

HashRouter

  • URLの例: https://example.com/#/about
  • 特徴:
    • URLに # が含まれます。
    • サーバー側の設定は不要です。サーバーはURLの仕組み上#より前の部分しか見ないため、どのURLにアクセスしてもindex.htmlを返します。GitHub Pagesのような静的ホスティングサービスでよく使われます。

GitHub Pagesにおけるルーターの選択肢

冒頭で示したようにvite.config.jsでbase: '/blogApp/'と設定した場合、Viteはアプリケーションのすべてのパスの先頭にこの文字列を付け加えます。

しかし、BrowserRouterはデフォルトではこの設定を知りません。
そのため、App.jsxで<Route path="/" element={<HomePage />} />と書いた場合、何も設定をしなければ/のパスは、実際のURLで/blogApp/ではなく/にマッチしようとしてしまいます。

結果としてルートにindex.htmlが存在しないため404エラーを返します。

この問題を解決するためには、以下の2つの方法があります。

HashRouterを使用する

この方法が最も簡単で確実です。HashRouterを利用するだけで、他の設定は一切不要です。

App.jsx

// App.jsx (HashRouterを使用する場合)  
import { HashRouter, Routes, Route } from 'react-router-dom';
import HomePage from './Components/HomePage';
import Login from './Components/Login';

const App = () => {
  return (
  <HashRouter>
    <Routes>
      <Route path="/" element={<HomePage />} />
      <Route path="/login" element={<Login />} />
    </Routes>
  </HashRouter>
  )
}

export default App

BrowserRouterを使用する

BrowserRouterを使いたい場合は、GitHub Pages上でリロードしてもindex.htmlが返されるように、404.htmlファイルを作成してリダイレクトするといった追加の工夫が必要になります。これはReact Routerの範疇を超えた対応のため本記事では省略します。

結論

ReactとViteの設定は、一見するとそれぞれ独立しているように見えますが、デプロイ先によっては密接に関連しています。今回のように、GitHub Pagesにデプロイする場合はHashRouterを使用するのが最も安全で確実です。

もしクリーンなURLが必要な場合は、BrowserRouterをサポートするサーバー設定が可能な別のホスティングサービスを検討した方が良さそうです。

Discussion