react-router browserHistory刷新頁面404問題解決
使用React開發新項目時,遇見了刷新頁面,直接訪問二級或三級路由時,訪問失敗,出現404或資源載入異常的情況,本篇針對此問題進行分析並總結解決方案。
react-router browserHistory刷新頁面404問題解決背景
使用webpack-dev-server做本地開發伺服器時,正常情況只需要簡單使用webpack-dev-server
指令啟動即可,但是當項目處於以下兩種情況時,往往需要有嵌套路由和非同步載入路由:
- 我們使用react-router這種路由庫構建單頁面應用路由;
- 使用
html-webpack-plugin
插件動態將載入js的<script>
標籤注入html文檔;
這時,訪問localhost:9090
是可以正常載入頁面和js等文件的,但是當我們需要訪問二級甚至三級路由或者刷新頁面時,如localhost:9090/posts/92
時,可能會出現兩種情況:
- 頁面載入失敗,返回
Cannot Get(404)
; - 服務響應,但是沒有返回webpack處理輸出的html文件,導致無法載入js資源,第二種情況如圖:
那麼我們怎麼處理才能正常訪問,各頁面路由呢?博主追蹤溯源,查找文檔配置後解決了問題,本篇就是對整個解決問題過程的總結。
分析問題
發現問題後,我們就要開始分析,解決問題了,我們判斷這個問題一般是兩方面原因造成:
- react-router路前端由配置;
- webpack-dev-server服務配置;
react-router
因為前端路由更容易確定問題,更方便分析,而且對於react-router更熟悉,所以首先去查詢react-router路由庫相關配置信息,發現文檔中提到了使用browserHistory
時,會創建真實的URL,處理初始/
請求沒有問題,但是對於跳轉路由後,刷新頁面或者直接訪問該URL時,會發現無法正確相應,更多信息查看參考文檔,文檔中也提供了幾種伺服器配置解決方式:
NODE
const express = require(express)const path = require(path)const port = process.env.PORT || 8080const app = express()// 通常用於載入靜態資源app.use(express.static(__dirname + /public))// 在你應用 JavaScript 文件中包含了一個 script 標籤// 的 index.html 中處理任何一個 routeapp.get(*, function (request, response){ response.sendFile(path.resolve(__dirname, public, index.html))})app.listen(port)console.log("server started on port " + port)
在使用Node作為服務時,需要使用通配符*
監聽所有請求,返回目標html文檔(引用js資源的html)。
NGINX
如果使用的是nginx伺服器,則只需要使用try_files 指令:
server { ... location / { try_files $uri /index.html }}
APACHE
如果使用Apache伺服器,則需要在項目根目錄創建.htaccess
文件,文件包含如下內容:
RewriteBase /RewriteRule ^index.html$ - [L]RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteRule . /index.html [L]
以下都是針對伺服器的配置,可惜的是我們目前還沒引入相關伺服器,只是使用了webpack-dev-server的內置服務,但是我們已經找到問題所在了,就是路由請求無法匹配返回html文檔,所以接下來就該去webpack-dev-server文檔中查找解決方式了。
webpack-dev-server
在這裡不得不吐槽一下webpack-dev-server官方文檔,博主反覆看了幾遍,才看清楚了問題所在,這裡也分兩種情況:
- 沒有修改
output.publicPath
,即webpack配置文件中沒有聲明值,屬於默認情況; - 設置了
output.publicPath
為自定義值;
點此查看文檔
默認情況
默認情況下,沒有修改output.publicPath
值,只需要設置webpack-dev-server的historyApiFallback
配置:
devServer: { historyApiFallback: true}
If you are using the HTML5 history API you probably need to serve your
如果你的應用使用HTML5 history API,你可能需要使用index.html
in place of 404 responses, which can be done by settinghistoryApiFallback: true
index.html
響應404或者問題請求,只需要設置ghistoryApiFallback: true
即可
自定義值
However, if you have modified
如果你在webpack配置文件中修改了output.publicPath
in your Webpack configuration, you need to specify the URL to redirect to. This is done using thehistoryApiFallback.index
optionoutput.publicPath
值,那麼你就需要聲明請求重定向,配置historyApiFallback.index
值。
// output.publicPath: /assets/historyApiFallback: { index: /assets/}
PROXY
發現使用以上方式,並不能完全解決我的問題,總會有路由請求響應異常,於是博主繼續查找更好的解決方案:
點此查看文檔
The proxy can be optionally bypassed based on the return from a function. The function can inspect the HTTP request, response, and any given proxy options. It must return either
代理提供通過函數返回值響應請求方式,針對不同請求進行不同處理,函數參數接收HTTP請求和響應體,以及代理配置對象,這個函數必須返回false或URL路徑,以表明如何繼續處理請求,返回URL時,源請求將被代理到該URL路徑請求。false
or a URL path that will be served instead of continuing to proxy the request.
proxy: { /: { target: https://api.example.com, secure: false, bypass: function(req, res, proxyOptions) { if (req.headers.accept.indexOf(html) !== -1) { console.log(Skipping proxy for browser request.); return /index.html; } } }}
如上配置,可以監聽https://api.example.com
域下的/
開頭的請求(等效於所有請求),然後判斷請求頭中accept
欄位是否包含html
,若包含,則代理請求至/index.html
,隨後將返回index.html文檔至瀏覽器。
解決問題
綜合以上方案,因為在webpack配置中修改了output.publicPath
為/assets/
,所以博主採用webpack-dev-server Proxy代理方式解決了問題:
const PUBLICPATH = /assets/...proxy: { /: { bypass: function (req, res, proxyOptions) { console.log(Skipping proxy for browser request.) return `${PUBLICPATH}/index.html` } }}
監聽所有前端路由,然後直接返回${PUBLICPATH}/index.html
,PUBLICPATH
就是設置的output.publicPath
值。
另外,博主總是習慣性的聲明,雖然不設置該屬性也能滿足預期訪問效果:
historyApiFallback: true
推薦閱讀:
※React系列:React架構
※React異常處理
※我對Flexbox布局模式的理解
※美化上傳文件組件
TAG:React | reactrouter |