使用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
复制代码