Vue.jsでのhistoryモードについて
今回はVue.jsでのhistoryモードについて記載しようと思います。
というのも、vue-routerを使ってページリロードしたときに404になることがありました。(historyモードの設定をしていました)
調べてみるとhistoryモードが関連しているようでした。
vue-routerのモードについて
まずは、vue-routerではhistoryモードについて説明します。 historyモードには数種類あります。
hashモード
まずはhashモード
についてです。
hashモードではURLが「http://localhost:8080/#/sample」のようにhash付きで表示されます。
hashモードはルーティングにURL hashを使用しています。 この形式で入力されるとvue-routerは、urlの#を見つけてそれより先の文字列を元に動的にコンポーネントを出し分けます。
例えば、http://localhost:8080/#/sampleというURLの場合、ブラウザはこのURLのフラグメント部分("/sample")を認識し、Vue Routerがこれを処理します。 しかし、実際にサーバーに送信されるURLは、http://localhost:8080/ です
つまり、サーバーにはフラグメント部分が含まれないため、特別な設定をする必要がありません。ブラウザ側で完結して処理されるため、サーバー側の設定は不要です。
HTML5モード
一方で、HTML5モード
ではURLは「http://localhost:8080/sample」のようにhashは無しで表示されます。
HTML5モードではlocalhostにリクエストを送信する際は、ローカルサーバがindex.htmlを返すようになっています。 ホスティングサービスを利用する際は、index.htmlを返すように設定しなければならないので注意が必要です。
vue-router のデフォルトは hashモードですが、下記のようにモードを変更することはできます。(index.jsに定義)
const router = createRouter({ history: createWebHistory(), routes, }); export default router
createWebHistory()はHTML5モードを指定します。
404になる仕組み
では、vue-routerを使用したときにリロードで404になるのはなぜでしょうか?
先ほども少し触れていますが、リロードで404になるのはHTML5モードのときです。 hashモードでは発生しません。
また、ローカルで開発時は発生しないはずで、ホスティングサービスを使用してデプロイしたら発生します。
一般的な Vue アプリケーションでは、 index.htmlと呼ばれる単一の HTML エントリーポイントがあります。 サーバーは、すべてのリクエストに対して index.htmlにリダイレクトして、ルーティングの処理を行う必要があります。
ローカル環境では、Vite(またはwebpack)開発サーバーが処理してくれますが、本番環境ではWebサーバーを自分で構成する必要があります。
index.htmlビルドを実行すると、dist/index.html
というビルド済みコピーが作成されるはずです。
本番環境では、dist/index.htmlのみ存在することになります。
例えばログイン画面(https://sample.com/login)に直接アクセスしようとした場合、ユーザからのアクセスを受けたサーバはindex.htmlを経由せずlogin.htmlへリクエストを行おうとします。
ただし、アプリはSPAで実装しているためデプロイをしている実体ファイルはjsやcssや画像ファイルなど除いてindex.htmlのみです。
そのためリクエストを受けるlogin.htmlが存在しないので、ページリロードや直接URLを入力してアクセスするした際に404となります。
vue-routerを使ってページリロードしたときの404対策
ここまでで404になる仕組みはわかったと思いますが、じゃあどうすればいいのでしょうか?
公式に記載はあるのですが、サーバーでの対策が必要になります。
URLがどの静的なアセットにもマッチしなかった時はindex.htmlページで受け付けるように設定します。
どのサーバーを使用しているかによって変わるので、公式のドキュメントにあるApacheの設定とVercelでのRewriteのリンクを参照に載せています。
サーバー側のWebサーバー設定で、存在しないURLをVueアプリのエントリーポイントにリダイレクトする設定を行っています。
リンクにご自身で使用しているサーバーの対策の記載がない場合には、Rewriteで検索したら見つかると思いますので試してみてください!
番外編
ここまでで、ページリロードしたときに404にならないように設定を変更しました。 ただ、意図的に404の場合にはカスタマイズした画面を表示したい場合があると思います。 例えば、削除されたデータを参照しようとするとデータがないので、404のエラーページを表示するなどです。
その場合に404でもindex.htmlを見るので初期ページが表示されるはずです。 そうならないように下記のようなルーティングを追加できます。
const router = new VueRouter({ mode: 'history', routes: [{ path: '*', component: NotFoundComponent }] })
URLをチェックして、マッチしなかった場合に 404 を返答します。 「*」でワイルドカードのパスを作り、どのパスにもマッチしなかった時のビューに飛ばすことができます。