目前比較主流的框架如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對應的服務器上做它的一些配置,再進行訪問。??