本文主要解决了以下几个问题:
webpack-dev-server配置后,访问页面出现GET /xxx
时如何解决
webpack-dev-server中historyApiFallback的作用
为什么不正确的配置会导致页面404
webpack-dev-server中publicPath的作用
为了写react demo,搭了一个简洁版的环境,因此webpack配置极其简单:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: {
app: path.join(__dirname, "../app/index.js")
},
output: {
filename: "app.js",
path: path.join(__dirname, "../dist")
},
resolve: {
extensions: [".js", ".jsx"]
},
module: {
rules: [
{
test: /\.js[x]?$/,
use: "babel-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "../app/template.html"),
filename: "index.html"
})
],
devServer: {
host: "0.0.0.0",
port: 5566
}
}
写了个首页,访问之后,页面ok,路由如下:
<BrowserRouter>
<Switch>
<Route exact path="/" component={ Home } />
</Switch>
</BrowserRouter>
然后又写了个列表页:
<Route exact path="/list" component={ List } />
但是却发现页面无法访问,页面报错信息为:Cannot GET /list
。网上搜索了解决方案,主要有两个:
方法一: 将BrowserRouter更换为HashRouter
方法二: webpack中devServer添加historyApiFallback: true
的配置
但是为什么这样配置就可以解决访问呢?
首先,看下webpack官网上关于historyApiFallback的说明:当使用HTML5 Histroy API时,任意的404响应都可能需要被替代为index.html,通过historyApiFallback: true
启用。意思就是说,任意404响应都表现为直接渲染为http://localhost:5566/index.html
,因为经过webpack打包后,在渲染模版中会引入打包后的js文件,上面设置了template输出文件名为index.html,所以在index.html文件中会引入<script type="text/javascript" src="app.js"></script>
,查找页面的逻辑在js文件中。所有如果我们将模版文件的输出文件名换成其它,那么页面还是404,如:
...
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "../app/template.html"),
filename: "test.html"
})
]
...
如果这样设置,那么即使在devServer中添加了historyApiFallback: true
的配置,访问http://localhost:5566/list
时,页面仍然会显示Cannot GET /list
。因为当404时,访问所有地址都会去渲染页面http://localhost:5566/index.html
,但是在打包文件找不到该文件,它显示是http://localhost:5566/test.html
,如果还想要访问的话,可以再将historyApiFallback
进行修改:
historyApiFallback: {
index: "/test.html"
}
更多设置可反问webpack官网
下面我们再来解决一个最重要的问题,为什么一开始的配置,访问页面的时候会404?
stackoverflow上有个回答很详细。
首先,要知道有一个服务端渲染
和客户端渲染
的概念。其它,还需要知道,webpack-dev-server实质是提供了一个简单的web服务器,本质上是一个小型的Node.js Express服务器。那么使用webpack-dev-server后,访问页面实际上走的是一个服务端渲染的过程,如果最后找不到要渲染的内容,那么就会报404。
方法一中将BrowserRouter改为HashRouter,页面的url就变成http://localhost:5566/#/xxx
的形式,在hash标识(#)后的部分不会被发送到服务器端,服务器只接收到http://localhost:5566
,返回首页以及包含React、React-Router的js代码,而访问其它页面时,走的是客户端渲染。
好了,现在通过设置historyApiFallback为true
,解决了页面无法访问的问题。然后我想要价格列表详情页,如果如下:
<Route exact path="/list/detai" component={ Detail } />
然后发现页面又不能访问了,浏览器报错404:GET http://localhost:5566/list/app.js 404 (Not Found)
。这又是为什么呢?经过资料查找,发现webpack的output中有一个配置项publicPath
需要注意。publicPath指定在浏览器中所引用的「此输出目录对应的公开URL」,默认值为一个空字符串""。也就是说,如果不设置这个值,那么在模版文件(index.html)中引入的js文件地址为:
<script src="app.js"></script>
所有,当页面访问http://localhost:5566/list/detail
时,其中引用的js文件为http://localhost:5566/list/aap.js
,而这个文件是不存在的,因为内存中这个文件和模版文件(index.html)都在dist的根目录下。因此,为了解决这一问题,需要手动设置publicPath的值:pulicPath: "/"
,这个模版文件(index.html)中引入的js文件地址为:
<script src="/app.js"></script>
当页面访问http://localhost:5566/list/detail
时,其中引用的js文件为http://localhost:5566/aap.js
。
至此,在该环境下的页面,都能正常访问了。