react-router browserHistory刷新頁(yè)面404問(wèn)題解決方法
使用React開(kāi)發(fā)新項(xiàng)目時(shí),遇見(jiàn)了刷新頁(yè)面,直接訪問(wèn)二級(jí)或三級(jí)路由時(shí),訪問(wèn)失敗,出現(xiàn)404或資源加載異常的情況,本篇針對(duì)此問(wèn)題進(jìn)行分析并總結(jié)解決方案。
背景
使用webpack-dev-server做本地開(kāi)發(fā)服務(wù)器時(shí),正常情況只需要簡(jiǎn)單使用webpack-dev-server指令啟動(dòng)即可,但是當(dāng)項(xiàng)目處于以下兩種情況時(shí),往往需要有嵌套路由和異步加載路由:
- 我們使用react-router這種路由庫(kù)構(gòu)建單頁(yè)面應(yīng)用路由;
- 使用html-webpack-plugin插件動(dòng)態(tài)將加載js的<script>標(biāo)簽注入html文檔;
這時(shí),訪問(wèn)localhost:9090是可以正常加載頁(yè)面和js等文件的,但是當(dāng)我們需要訪問(wèn)二級(jí)甚至三級(jí)路由或者刷新頁(yè)面時(shí),如localhost:9090/posts/92時(shí),可能會(huì)出現(xiàn)兩種情況:
- 頁(yè)面加載失敗,返回Cannot Get(404);
- 服務(wù)響應(yīng),但是沒(méi)有返回webpack處理輸出的html文件,導(dǎo)致無(wú)法加載js資源,第二種情況如圖:

那么我們?cè)趺刺幚聿拍苷TL問(wèn),各頁(yè)面路由呢?博主追蹤溯源,查找文檔配置后解決了問(wèn)題,本篇就是對(duì)整個(gè)解決問(wèn)題過(guò)程的總結(jié)。
分析問(wèn)題
發(fā)現(xiàn)問(wèn)題后,我們就要開(kāi)始分析,解決問(wèn)題了,我們判斷這個(gè)問(wèn)題一般是兩方面原因造成:
- react-router路前端由配置;
- webpack-dev-server服務(wù)配置;
react-router
因?yàn)榍岸寺酚筛菀状_定問(wèn)題,更方便分析,而且對(duì)于react-router更熟悉,所以首先去查詢(xún)r(jià)eact-router路由庫(kù)相關(guān)配置信息,發(fā)現(xiàn)文檔中提到了使用browserHistory時(shí),會(huì)創(chuàng)建真實(shí)的URL,處理初始/請(qǐng)求沒(méi)有問(wèn)題,但是對(duì)于跳轉(zhuǎn)路由后,刷新頁(yè)面或者直接訪問(wèn)該URL時(shí),會(huì)發(fā)現(xiàn)無(wú)法正確相應(yīng),更多信息查看參考文檔,文檔中也提供了幾種服務(wù)器配置解決方式:
Node
const express = require('express')
const path = require('path')
const port = process.env.PORT || 8080
const app = express()
// 通常用于加載靜態(tài)資源
app.use(express.static(__dirname + '/public'))
// 在你應(yīng)用 JavaScript 文件中包含了一個(gè) script 標(biāo)簽
// 的 index.html 中處理任何一個(gè) route
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})
app.listen(port)
console.log("server started on port " + port)
在使用Node作為服務(wù)時(shí),需要使用通配符*監(jiān)聽(tīng)所有請(qǐng)求,返回目標(biāo)html文檔(引用js資源的html)。
Nginx
如果使用的是nginx服務(wù)器,則只需要使用try_files 指令:
server {
...
location / {
try_files $uri /index.html
}
}
Apache
如果使用Apache服務(wù)器,則需要在項(xiàng)目根目錄創(chuàng)建.htaccess文件,文件包含如下內(nèi)容:
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
以下都是針對(duì)服務(wù)器的配置,可惜的是我們目前還沒(méi)引入相關(guān)服務(wù)器,只是使用了webpack-dev-server的內(nèi)置服務(wù),但是我們已經(jīng)找到問(wèn)題所在了,就是路由請(qǐng)求無(wú)法匹配返回html文檔,所以接下來(lái)就該去webpack-dev-server文檔中查找解決方式了。
webpack-dev-server
在這里不得不吐槽一下webpack-dev-server官方文檔,博主反復(fù)看了幾遍,才看清楚了問(wèn)題所在,這里也分兩種情況:
- 沒(méi)有修改output.publicPath,即webpack配置文件中沒(méi)有聲明值,屬于默認(rèn)情況;
- 設(shè)置了output.publicPath為自定義值;
默認(rèn)情況
默認(rèn)情況下,沒(méi)有修改output.publicPath值,只需要設(shè)置webpack-dev-server的historyApiFallback配置:
devServer: {
historyApiFallback: true
}
If you are using the HTML5 history API you probably need to serve your index.html in place of 404 responses, which can be done by setting historyApiFallback: true
如果你的應(yīng)用使用HTML5 history API,你可能需要使用index.html響應(yīng)404或者問(wèn)題請(qǐng)求,只需要設(shè)置g historyApiFallback: true即可
自定義值
However, if you have modified output.publicPath in your Webpack configuration, you need to specify the URL to redirect to. This is done using the historyApiFallback.index option
如果你在webpack配置文件中修改了 output.publicPath 值,那么你就需要聲明請(qǐng)求重定向,配置historyApiFallback.index 值。
// output.publicPath: '/assets/'
historyApiFallback: {
index: '/assets/'
}
Proxy
發(fā)現(xiàn)使用以上方式,并不能完全解決我的問(wèn)題,總會(huì)有路由請(qǐng)求響應(yīng)異常,于是博主繼續(xù)查找更好的解決方案:
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 false or a URL path that will be served instead of continuing to proxy the request.
代理提供通過(guò)函數(shù)返回值響應(yīng)請(qǐng)求方式,針對(duì)不同請(qǐng)求進(jìn)行不同處理,函數(shù)參數(shù)接收HTTP請(qǐng)求和響應(yīng)體,以及代理配置對(duì)象,這個(gè)函數(shù)必須返回false或URL路徑,以表明如何繼續(xù)處理請(qǐng)求,返回URL時(shí),源請(qǐng)求將被代理到該URL路徑請(qǐng)求。
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';
}
}
}
}
如上配置,可以監(jiān)聽(tīng)https://api.example.com域下的/開(kāi)頭的請(qǐng)求(等效于所有請(qǐng)求),然后判斷請(qǐng)求頭中accept字段是否包含html,若包含,則代理請(qǐng)求至/index.html,隨后將返回index.html文檔至瀏覽器。
解決問(wèn)題
綜合以上方案,因?yàn)樵趙ebpack配置中修改了output.publicPath為/assets/,所以博主采用webpack-dev-server Proxy代理方式解決了問(wèn)題:
const PUBLICPATH = '/assets/'
...
proxy: {
'/': {
bypass: function (req, res, proxyOptions) {
console.log('Skipping proxy for browser request.')
return `${PUBLICPATH}/index.html`
}
}
}
監(jiān)聽(tīng)所有前端路由,然后直接返回${PUBLICPATH}/index.html,PUBLICPATH就是設(shè)置的output.publicPath值。
另外,博主總是習(xí)慣性的聲明,雖然不設(shè)置該屬性也能滿足預(yù)期訪問(wèn)效果:
historyApiFallback: true
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
react國(guó)際化化插件react-i18n-auto使用詳解
這篇文章主要介紹了react國(guó)際化化插件react-i18n-auto使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
React學(xué)習(xí)之受控組件與數(shù)據(jù)共享實(shí)例分析
這篇文章主要介紹了React學(xué)習(xí)之受控組件與數(shù)據(jù)共享,結(jié)合實(shí)例形式分析了React受控組件與組件間數(shù)據(jù)共享相關(guān)原理與使用技巧,需要的朋友可以參考下2020-01-01
Express+React+Antd實(shí)現(xiàn)上傳功能(前端和后端)
這篇文章主要介紹了Express+React+Antd實(shí)現(xiàn)上傳功能(前端和后端),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04
React實(shí)現(xiàn)核心Diff算法的示例代碼
這篇文章主要為大家詳細(xì)介紹了React如何實(shí)現(xiàn)Diff算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-04-04
react-native 實(shí)現(xiàn)購(gòu)物車(chē)滑動(dòng)刪除效果的示例代碼
這篇文章主要介紹了react-native 實(shí)現(xiàn)購(gòu)物車(chē)滑動(dòng)刪除效果的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
React實(shí)現(xiàn)翻頁(yè)時(shí)鐘的代碼示例
本文給大家介紹了React實(shí)現(xiàn)翻頁(yè)時(shí)鐘的代碼示例,翻頁(yè)時(shí)鐘把數(shù)字分為上下兩部分,翻頁(yè)效果的實(shí)現(xiàn)需要通過(guò)設(shè)置 position 把所有的數(shù)組放在同一個(gè)位置疊加起來(lái),文中有詳細(xì)的代碼講解,需要的朋友可以參考下2023-08-08

