- 首页
- 其他开发
- React Router v4 嵌套路由不适用于 webpack-dev-server
React Router v4 嵌套路由不适用于 webpack-dev-server
[英] React Router v4 nested routes not work with webpack-dev-server
本文介绍了React Router v4 嵌套路由不适用于 webpack-dev-server的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我尝试像这样为我的 React 应用程序设置嵌套路由
/
-> 主页/about
-> 关于页面/protected
-> 受保护的默认页面/protected/page1
-> 受保护的页面 1
它在代码和盒子中工作正常(https://codesandbox.io/s/react-router-nested-route-utqy7) React 16.8.1 React Router 4.3.1
但是当我用 webpack-dev-server (3.7.1) 设置同样的东西时,它只能到达 /
而无法到达其他路由.
我的文件结构就像
├── package.json├── src│ ├── index.jsx│ └── index.html├── webpack│ ├── path.js│ ├── webpack.common.js│ └── webpack.dev.js└── webpack.config.js
paths.js
const path = require('path');模块.出口 = {outputPath: path.resolve(__dirname, '../', 'build'),entryPath: path.resolve(__dirname, '../', 'src/index.jsx'),templatePath: path.resolve(__dirname, '../', 'src/index.html'),};
webpack.common.js
const webpack = require('webpack');const convert = require('koa-connect');const history = require('connect-history-api-fallback');const HtmlWebpackPlugin = require('html-webpack-plugin');const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');const commonPaths = require('./paths');模块.出口 = {条目:commonPaths.entryPath,模块: {规则: [{测试:/\.(js|jsx)$/,loader: 'babel-loader',排除:/(节点模块)/,},],},服务: {添加:应用程序=>{app.use(convert(history()));},内容:commonPaths.entryPath,开发:{publicPath: commonPaths.outputPath,},打开:真实,},解决: {模块:['src','node_modules'],扩展名:['*', '.js', '.jsx', '.css', '.scss'],},插件: [新的 webpack.ProgressPlugin(),新的 HtmlWebpackPlugin({模板:commonPaths.templatePath,}),新的 ScriptExtHtmlWebpackPlugin({默认属性:'异步',}),],};
webpack.dev.js
const webpack = require('webpack');const commonPaths = require('./paths');模块.出口 = {模式:'发展',输出: {文件名:'[名称].js',路径:commonPaths.outputPath,chunkFilename: '[name].js',},模块: {规则: [{测试:/\.(css|scss)$/,用: ['样式加载器',{加载器:'css-加载器',},'sass-loader',],},],},开发服务器:{contentBase: commonPaths.outputPath,压缩:真的,热:真的,},插件:[新的 webpack.HotModuleReplacementPlugin()],};
webpack.config.js
const webpackMerge = require('webpack-merge');const common = require('./webpack/webpack.common');const devConfig = require(`./webpack/webpack.dev.js`);module.exports = webpackMerge(common, devConfig);
index.jsx
从react"导入React;从react-dom"导入{渲染};从react-router-dom"导入 { BrowserRouter, Route };const 主页 = () =>(<div><h1>主页</h1>
);const AboutPage = () =>(<div><h1>关于</h1>
);const 受保护 = () =>(<div><h1>受保护的默认页面</h1>
);const ProtectedPage1 = () =>(<div><h1>ProtectedPage1</h1>
);使成为(<浏览器路由器><div><Route path="/" component={Homepage} 精确/><Route path="/about" component={AboutPage}/><路线路径=/受保护"render={({ 匹配:{ url } }) =>(<div><Route path={`${url}/`} component={Protected} 精确/><路由路径={`${url}/page1`} component={ProtectedPage1}/>
)}/>
</BrowserRouter>,document.getElementById('app'));
我认为我的配置中有些路径不正确,我就是想不通哪里错了.
解决方案
我终于找到了 webpack-dev-server 无法提供嵌套路由的原因.
作为单页应用程序,当您访问 React 应用程序的 /somepath
时,它实际上回退到 /
并将路径名传递给 react 路由器.React 路由器将通过使用浏览器的历史 API 将您导航到 /somepath
.
webpack-dev-server,由于某种未知原因,默认情况下不会启用这种回退到历史 API"行为.
所以,我们需要在webpack配置的devServer
中添加historyApiFallback: true,
.
现在,所有顶级路由,比如 /somepath
都应该可以工作,但是对于嵌套路由,比如 /somepath/morepath
,这还不够.
使用默认的 webpack-dev-server 设置,编译的 html 模板将指向捆绑的 js,如 <script type="text/javascript" src="main.js"></script>代码>.注意 src="main.js"
,它假定 main.js
与 index.html
位于同一路径下.该假设对于顶级路径 /somepath
是正确的,但对于嵌套路由 /somepath/morepath
,此假设将导致 html 文件访问 main.js
就像 /somepath/main.js
.
所以,我们最终要寻找一种方法,当它访问捆绑的js时,为html文件指定某个位置.而且,这是publicPath
的工作.将 publicPath: '/',
添加到 webpack 配置的输出块.它会告诉 html 总是从 /
文件夹访问 main.js
并且编译的 html 将是 <script type="text/javascript" src="/main.js"></script>
.这正是我们正在寻找的.p>
I try to setup nested routes for my react app like this
/
-> Home Page
/about
-> About Page
/protected
-> protected default page
/protected/page1
-> protected page 1
It works fine in codesandbox (https://codesandbox.io/s/react-router-nested-route-utqy7) React 16.8.1 React Router 4.3.1
But when I set the same thing up with webpack-dev-server (3.7.1), it can only reach /
and can't reach to the rest routes.
My file structure is like
├── package.json
├── src
│ ├── index.jsx
│ └── index.html
├── webpack
│ ├── paths.js
│ ├── webpack.common.js
│ └── webpack.dev.js
└── webpack.config.js
paths.js
const path = require('path');
module.exports = {
outputPath: path.resolve(__dirname, '../', 'build'),
entryPath: path.resolve(__dirname, '../', 'src/index.jsx'),
templatePath: path.resolve(__dirname, '../', 'src/index.html'),
};
webpack.common.js
const webpack = require('webpack');
const convert = require('koa-connect');
const history = require('connect-history-api-fallback');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const commonPaths = require('./paths');
module.exports = {
entry: commonPaths.entryPath,
module: {
rules: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /(node_modules)/,
},
],
},
serve: {
add: app => {
app.use(convert(history()));
},
content: commonPaths.entryPath,
dev: {
publicPath: commonPaths.outputPath,
},
open: true,
},
resolve: {
modules: ['src', 'node_modules'],
extensions: ['*', '.js', '.jsx', '.css', '.scss'],
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({
template: commonPaths.templatePath,
}),
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'async',
}),
],
};
webpack.dev.js
const webpack = require('webpack');
const commonPaths = require('./paths');
module.exports = {
mode: 'development',
output: {
filename: '[name].js',
path: commonPaths.outputPath,
chunkFilename: '[name].js',
},
module: {
rules: [
{
test: /\.(css|scss)$/,
use: [
'style-loader',
{
loader: 'css-loader',
},
'sass-loader',
],
},
],
},
devServer: {
contentBase: commonPaths.outputPath,
compress: true,
hot: true,
},
plugins: [new webpack.HotModuleReplacementPlugin()],
};
webpack.config.js
const webpackMerge = require('webpack-merge');
const common = require('./webpack/webpack.common');
const devConfig = require(`./webpack/webpack.dev.js`);
module.exports = webpackMerge(common, devConfig);
index.jsx
import React from "react";
import { render } from "react-dom";
import { BrowserRouter, Route } from "react-router-dom";
const Homepage = () => (
<div>
<h1>Home Page</h1>
</div>
);
const AboutPage = () => (
<div>
<h1>About</h1>
</div>
);
const Protected = () => (
<div>
<h1>Protected default page</h1>
</div>
);
const ProtectedPage1 = () => (
<div>
<h1>ProtectedPage1</h1>
</div>
);
render(
<BrowserRouter>
<div>
<Route path="/" component={Homepage} exact />
<Route path="/about" component={AboutPage} />
<Route
path="/protected"
render={({ match: { url } }) => (
<div>
<Route path={`${url}/`} component={Protected} exact />
<Route path={`${url}/page1`} component={ProtectedPage1} />
</div>
)}
/>
</div>
</BrowserRouter>,
document.getElementById('app')
);
I think some paths are incorrect in my config, I just can't figure out where is wrong.
解决方案
I finally figured out the reason that webpack-dev-server couldn't serve nested routes.
As a single page application, when you visit /somepath
of your react app, it actually fallback to the /
and pass the pathname to react router. React router will navigate you to /somepath
by the using browser's history API.
webpack-dev-server, for some unknown reason, doesn't enable this "fallback to history API" behaviour by default.
So, we need to add historyApiFallback: true,
to the devServer
of webpack config.
Now, all top level routes, like /somepath
should work, but for nested routes, like /somepath/morepath
, it's not enough.
With default webpack-dev-server setting, the compiled html template will point to the bundled js like <script type="text/javascript" src="main.js"></script>
. Pay attention to the src="main.js"
which assumes the main.js
is under the same path as the index.html
. The assumption is true for top level path /somepath
but for nested routes, /somepath/morepath
, this assumption will lead html file to access main.js
like /somepath/main.js
.
So, we end up with looking for a way to specify a certain place for html file when it's going to access the bundled js. And, it's the job of publicPath
. Adding publicPath: '/',
to the output block of webpack config. It will tell html to always access main.js
from /
folder and the compiled html will be <script type="text/javascript" src="/main.js"></script>
. That's exactly what we're looking for.
这篇关于React Router v4 嵌套路由不适用于 webpack-dev-server的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文