はじめに
React Router DOMって、便利ですよね。
でも、直接URLにアクセスしたとき、
ただの静的ホスティングだと404 Not Foundになってしまいます。
そこで、今回は無料で使える静的ホスティングサービスGitHub PagesとReact Router DOMを組み合わせて、404が起こらないような構成を作ってみたいと思います。(無理やり)
多分、もっといい方法が存在すると思うので、その時はコメントで教えてくださいますと嬉しいです。
GitHub Pagesの仕様
GitHubのレポジトリにあるファイルをそのまま静的ホスティングしてくれるというものです。
GitHub Pagesに、パスが見つからなかったら。
/404.html
に飛んでくれるという仕様があります。
(カスタム404ページ作成機能)
これを利用します。
React Router DOMの仕様
React Router DOMとは、Reactのライブラリで、
一つのページを使って複数のページを再現するというものです。
仕組み
サイト内のどこかに飛ぼうとすると、JavaScriptが、
再読み込みさせずに、ブラウザに表示されるアドレスを変更し、
表示される内容を入れ替えています。
まるで、違うページに飛んだように見えます。
利点
- ページ遷移が非常に早い
- 静的ホスティングでも、動的なRoutingを作ることができる。
fetchをすることなく、JavaScriptで、表示内容を入れ替えているだけなので、ページ遷移がとても早い。
問題点
- 直接URLを入力して、ルート以外のページを見ようとすると、
あくまでも、React Router DOMによる模擬的なページであり、
404 Not Foundが発生する。 - ページに個別のheadを設定できないので、SEO対策がしにくい
したいこと
1つ目の問題点が、致命的すぎたので、無理やり回避しようと思う。
原理
/
にアクセスされたとき
→そのままReactを動かしてページを表示する(普通)
他にアクセスされたとき
→404.htmlが読み込まれるので、現在のパスをURLパラメータに保存した状態で、/
に飛ばす。
→/
のページが、パラメーターを読み取り、Reactに渡して、模擬的にページ遷移
実装例
404.htmlは、React projectの/public
に保存して、静的ホスティングしました。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="loader.css">
</head>
<body>
<!--簡単なローディング画面-->
<div id="loading">
<div class="loading-wrapper">
<div class="loader"></div>
</div>
<div class="title"><span></span></div>
</div>
</body>
<script>
// 現在のパスを取得
const path = window.location.pathname;
// パラメーター付きのルートページに遷移する
window.location = `/?path=${path}`;
</script>
</html>
window.location.pathname
で、
例えば、
https://*****.com/your/page/url
なら、
/your/page/url
を取得できます。
window.location = "TARGETPATH"
で、普通のページ遷移を起こせます。
↓React Projectのルートに保存する。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="root" style="display: none;"></div>
<script type="module" src="/src/main.tsx"></script>
<div id="loading">
<div class="loading-wrapper">
<div class="loader"></div>
</div>
<div class="title"><span></span></div>
</div>
</body>
<script>
// ロードが終わったらローディング画面を消して、Reactのルートを表示する
window.onload = () => {
document.getElementById("root").style.display = "block";
document.getElementById("loading").style.display = "none";
};
</script>
</html>
ルートに設置するページコンポーネント内に、以下。
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
function Home() {
// URLparameterを参照し、React内でページ遷移
const navigate = useNavigate();
useEffect(() => {
// URL parameter 取得
const query = window.location.search;
const paramsStr = query.split('?')
// パラメータがあったら
if (!(paramsStr.length <= 1)) {
// パラメータ取得
const params = paramsStr[1].split('&').map((item) => {
const [key, value] = item.split('=');
return { key: key, value: value };
});
// pathパラメータがあれば、そのパスに遷移
let targetPath: string | null = null;
params.forEach((item) => {
if (item.key == 'path') {
targetPath = item.value;
}
});
if (targetPath != null) {
// ページ遷移
navigate(targetPath);
}
}
}, [navigate]);
return (
<>
<h1>Home Page</h1>
</>
)
}
export default Home;
useNavigate Hock
は、ページ遷移を起こす関数を返します。
navigate
関数は、useEffect Hock
のなかで、記述しないと、
エラーが出るようです。
終わりに
結構、便利だと思います。
ただ、一度ルートページのレンダリングをReactが行うので、
そこが無駄になっています。
僕には、思いつきませんでした。
いつも使っている開発環境が、ネイティブなものじゃなかったので、
本番環境をあまり再現できず、デバッグに時間がかかりました。
このアイデアを、
このサイトを作る上で実際に使っています。
GitHubの方、ぜひご覧ください。
ソースは、↓
今回のコードを使ってるところ
/public/404.html
/src/pages/Home.tsx
/index.html