背景
いつものようにreact-router-dom
を利用していると急にCannot Get
とか言われて🤔になったので、備忘録として
残しておく。Why
とHow
を突き詰めることで知識と対応力をつけていきます。💪
ちなみにwebpack
を利用しているため、今回はそちらの解決方法を紹介するつもりです。
間違っている点や疑問点はビシビシコメントください🤖
Why
そもそも何でこんな問題が発生しているのかについて考えいきましょう!
ブラウザはhttps://sample.com/
にアクセスするとサーバーサイドに問い合わせが飛びます。
サーバーは/(index.html)
を求めるものだと判断してindex.html
をレスポンスします。
そのため、もしhttps://sample.com/hello.html
にアクセスして、hello.html
が存在しない、かつ特にNot Found
処理をしていない場合はCannot GET
になってしまいます。
今回の場合、/sample
にアクセスが入り、サーバーはsample
ファイルが見つからずこのような結果になってしまってます。
How
ここからは解決策を見ていきましょう!!
webpack
を利用している場合dist
等のプロジェクトフォルダーがビルドを行うことで、吐き出されていると思われます。
全てのURL
に対してhtmlファイルを吐き出すのはよくないでしょう。(SPAじゃない)
それではどのように解決するかというとwebpack.config
に次のような設定を追加しましょう!!
output: {
....
publicPath: '/'
},
devServer: {
...
historyApiFallback: true,
},
publicPath
とhistoryApiFallback
の2行を追加しただけで動くようになりました。
これら2つの設定がどのような動きをするのか見てみましょう。
publicPath
公式には次のようにあります。
The publicPath configuration option can be quite useful in a variety of scenarios. It allows you to specify the base path for all the assets within your application.
公式にあるようにCSS
やJavaScript
等のasset群に対して、ベースURLを提供します。 下記のようにpublic Path
を指定するとpathが変更されていることがわかります。
output: {
....
publicPath: 'https://asset.com/'
},
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Hello World</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://asset.com/styles/index.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script defer src="https://asset.com/scripts/bundle.js"></script></body>
</html>
url-loader
等に噛ませることも可能で、本番環境で画像等をCDNを利用して配信する場合、環境変数を通してローカルと本番環境でファイルpathの変更できます。
実は今回の問題に対してpublicPath
の設定が必要な場合と、そうでない場合の2種類があります。
ほとんどの場合publicPath
を指定しないで後述するhistoryApiFallback
を設定してあげることで動くのですが、プロジェクトのフォルダ構成やそれこそCDNの関係で必要となる場合があるので適宜設定してあげましょう。
historyApiFallback
When using the HTML5 History API, the index.html page will likely have to be served in place of any 404 responses. Enable devServer.historyApiFallback by setting it to true:
そのままですね。これをtrue
にしてあげることで、ファイルが見つからなった場合にindex.html
を返すようになります。
試しにプロジェクトファイルに2つのHTMLファイルを用意します。 この時の注意点ですが、HtmlWebpackPlugin
はデフォル設定だとfilename
をindex.html
で吐き出してしまうので、どちらかにfilename
を追加しましょう。
plugins: [
....
new HtmlWebpackPlugin({
template: path.resolve(SRC_PATH, "./index.html"),
inject: "body",
}),
new HtmlWebpackPlugin({
template: path.resolve(SRC_PATH, "./sample.html"),
filename: "sample.html",
}),
],
これで/sample.html
の場合はファイルが存在するので、https://hoge.com/sample.html
の場合はsample.html
がレスポンスされます。それ以外の場合はindex.html
がレスポンスされます。
historyApiFallback
を利用することでCannnot GET
問題は解決できます。