使用React開發新項目時,碰見了刷新頁面,直接訪問二級或三級路由時,訪問失敗,出現404或資源加載異常的狀況,本篇針對此問題進行分析並總結解決方案。javascript
查看我的博客html
使用webpack-dev-server作本地開發服務器時,正常狀況只須要簡單使用webpack-dev-server
指令啓動便可,可是當項目處於如下兩種狀況時,每每須要有嵌套路由和異步加載路由:前端
html-webpack-plugin
插件動態將加載js的<script>
標籤注入html文檔;這時,訪問localhost:9090
是能夠正常加載頁面和js等文件的,可是當咱們須要訪問二級甚至三級路由或者刷新頁面時,如localhost:9090/posts/92
時,可能會出現兩種狀況:java
Cannot Get(404)
;那麼咱們怎麼處理才能正常訪問,各頁面路由呢?博主追蹤溯源,查找文檔配置後解決了問題,本篇就是對整個解決問題過程的總結。react
發現問題後,咱們就要開始分析,解決問題了,咱們判斷這個問題通常是兩方面緣由形成:webpack
由於前端路由更容易肯定問題,更方便分析,並且對於react-router更熟悉,因此首先去查詢react-router路由庫相關配置信息,發現文檔中提到了使用browserHistory
時,會建立真實的URL,處理初始/
請求沒有問題,可是對於跳轉路由後,刷新頁面或者直接訪問該URL時,會發現沒法正確相應,更多信息查看參考文檔,文檔中也提供了幾種服務器配置解決方式:nginx
const express = require('express')
const path = require('path')
const port = process.env.PORT || 8080
const app = express()
// 一般用於加載靜態資源
app.use(express.static(__dirname + '/public'))
// 在你應用 JavaScript 文件中包含了一個 script 標籤
// 的 index.html 中處理任何一個 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做爲服務時,須要使用通配符*
監聽全部請求,返回目標html文檔(引用js資源的html)。git
若是使用的是nginx服務器,則只須要使用try_files
指令:github
server {
...
location / {
try_files $uri /index.html
}
}
複製代碼
若是使用Apache服務器,則須要在項目根目錄建立.htaccess
文件,文件包含以下內容:web
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
複製代碼
如下都是針對服務器的配置,惋惜的是咱們目前還沒引入相關服務器,只是使用了webpack-dev-server的內置服務,可是咱們已經找到問題所在了,就是路由請求沒法匹配返回html文檔,因此接下來就該去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
index.html
in place of 404 responses, which can be done by settinghistoryApiFallback: true
若是你的應用使用HTML5 history API,你可能須要使用
index.html
響應404或者問題請求,只須要設置ghistoryApiFallback: 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 thehistoryApiFallback.index
option若是你在webpack配置文件中修改了
output.publicPath
值,那麼你就須要聲明請求重定向,配置historyApiFallback.index
值。
// output.publicPath: '/assets/'
historyApiFallback: {
index: '/assets/'
}
複製代碼
發現使用以上方式,並不能徹底解決個人問題,總會有路由請求響應異常,因而博主繼續查找更好的解決方案:
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.代理提供經過函數返回值響應請求方式,針對不一樣請求進行不一樣處理,函數參數接收HTTP請求和響應體,以及代理配置對象,這個函數必須返回false或URL路徑,以代表如何繼續處理請求,返回URL時,源請求將被代理到該URL路徑請求。
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
複製代碼