目前比较主流的框架如Vue、React等,都是单页面应用的框架。一般我们在使用它们的时候,会使用官方脚手架来创建项目,所以我们不必关心单页面应用路由是如何实现的,因为脚手架中已经帮我们做好了配置。在具体项目开发中,我们只需要做相应的路由配置即可。
那么在实际项目中,手动搭建项目的前提下,我们需要如何解决单页面应用的路由问题呢???
看个?:
目录结构:
|--demo
|--src
|--index.html
|--index.js
|--home.js
|--list.js
|--node_modules
|--.babelrc
|--package-lock.json
|--package.json
|--webpack.config.js
webpack.config.js中的配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
entry: {
main: './src/index.js'
},
devServer: {
contentBase: './dist',
open: true,
port: 8081,
hot: true,
hotOnly: true
},
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
.babelrc配置:
{
"presets": [
[
"@babel/preset-env", {
"targets": {
"chrome": "67"
},
"useBuiltIns": "usage"
}
],
"@babel/preset-react"
]
}
package.json中scripts配置:
"scripts": {
"start": "webpack-dev-server"
}
src/index.js:
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom'; // 引入路由模块
import ReactDom from 'react-dom';
import Home from './home.js';
import List from './list.js';
// 这里设置的路由,根据用户的请求,来决定展示什么
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Route path='/' exact component={ Home } />
<Route path='/list' component={ List } />
</div>
</BrowserRouter>
)
}
}
ReactDom.render(<App />, document.getElementById('root'));
期望效果:
- 当用户访问根路径时,会访问home组件的内容
- 当用户访问/list路径时,会访问list组件的内容
src/home.js:
import React, { Component } from 'react';
class Home extends Component {
render() {
return <div>HomePage</div>
}
}
export default Home;
src/list.js:
import React, { Component } from 'react';
class List extends Component {
render() {
return <div>ListPage</div>
}
}
export default List;
注: 记得通过npm包管理工具一一安装上方所有配置项中的依赖、插件以及第三方库。
执行打包:
npm run start
唤起浏览器localhost:8081服务:
当访问"/list"路由时,我们期望出现ListPage的内容,但是实际情况如下:
注: 当我们去访问localhost:8081/list这个地址的时候,webpackDevServer会默认为你要访问服务器上的一个list页面。但我们的项目中只有一个index.html页面,并不存在list页面。所以它会提示你:Cannot GET /list (页面不存在)
我们可以使用webpackDevServer中的 historyApiFallback 配置来解决此问题:
devServer: {
contentBase: './dist',
open: true,
port: 8081,
hot: true,
hotOnly: true,
historyApiFallback: true // 在使用单页面应用的时候,需要设置此参数,代表如果访问除根路径以外的地址,最终都会转向去请求根路径。
},
- historyApiFallback: true 代表在使用单页面应用的时候,需要设置此参数,代表如果访问除根路径以外的地址,最终都会转向去请求根路径。
此时再执行打包:
npm run start
浏览器显示正常:
打开控制台网络项:
从 “/list” 路由请求的响应信息我们可以看出,当访问localhost:8081/list地址时,最终访问的仍然是index.html页面。到此,单页面应用路由问题已完美解决。
所以,当我们在使用单页面应用时,记得一定要在devServer中配置historyApiFallback: true配置项。
historyApiFallback 的详细配置
historyApiFallback: {
rewrites: [
{ from: /abc.html/, to: '/index.html' }
]
}
- rewrites 中的配置代表:当我访问locahost:8081/abc.html时,devServer会自动帮我们转向访问index.html页面。
- from 指访问的地址
- to 指devServer最终帮我们转向的地址
所以上面例子中的 historyApiFallback:true 也就等价于:
historyApiFallback: {
rewrites: [
{
from: /\.*/, // 访问任何地址
to: '/index.html' // 都转向index.html页面(根路径页面)
}
]
}
一般的项目中,当我们做单页面应用,配置单页面路由时,设置 historyApiFallback:true;即可解决。
注: historyApiFallback只是在我们的开发环境中(本地)有效,一旦代码上线,就会再次出现访问页面找不到的问题。这时就需要后端小伙伴配合,仿照webpack-dev-server的配置,在nginx或apache对应的服务器上做它的一些配置,再进行访问。??