React Router v4 使用Webpack-dev-server无法正确显示嵌套路由

8

我尝试为我的React应用程序设置嵌套路由,如下:

  • / -> 主页
  • /about -> 关于页面
  • /protected -> 受保护的默认页面
  • /protected/page1 -> 受保护的第一页

在CodeSandbox中(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
│   ├── 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')
);

我认为我的配置中有一些路径不正确,但我就是想不出哪里错了。

5个回答

44

我终于找到了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.jsindex.html在同一路径下。对于嵌套路由/somepath/morepath,这个假设将导致HTML文件访问类似于/somepath/main.jsmain.js

因此,我们需要寻找一种指定html文件访问打包JS的特定位置的方法,而这便是 `publicPath` 的工作。在webpack配置文件的输出块中添加 `publicPath: '/'` 配置项,它将告诉html总是从 `/` 文件夹访问 `main.js`,编译后的html将会是 ``。这正是我们要寻找的内容。

1
是的,这就是答案 文件名: 'main.js', 路径: path.resolve(__dirname, 'dist'), 公共路径: '/', },``` - elporfirio

19

尝试添加:

<base href="/" />

到你的index.html<head>标签中。这样它将始终查找/main.js捆绑包,即使在嵌套路由中也是如此。


4

我遇到了与问题描述中相同的问题(webpack-dev-server无法提供嵌套路由,但顶层路由运行正常)。不幸的是,historyApiFallback: truepublicPath: '/'都没有起作用。实际上,问题在于index.html文件中,更准确地说是在<script src="bundle.js"></script>标签内。将其更改为

<script src="/bundle.js"></script>       <!-- do include slash before the file name -->

足够终结疼痛。

这是真正帮助我的事情。将 publicPath 更改为 '/' 绝对没有任何影响,无论是在 devServer.publicPath 还是在 output.publicPath 中,而且 webpack 文档也说它是默认值。 - fires3as0n

3
总结一下 @Bing Lu 的回答,在你的 webpack.config.js 文件中:
module.exports = () => ({
  mode: 'development',
  entry: ...,
  ...,
  output: {
    ...
    publicPath: '/' // <- this is the important line along with historyApiFallback = true in the dev server config
  },
  ...,
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    historyApiFallback: true,
    compress: true,
    ...
  },
})

0

基本上,您可以使用<HashRouter>来包装您的React应用程序,而不是使用<BrowserRouter>,这样就不需要进行任何webpack配置修改。如果您不想使用HashRouter,则可以在webpack.config文件底部的web pack dev server配置中自由使用historyApiFallback: true。

like so
 const config = {

........
    devServer: {
        compress: true,
        port: 3000,
        https: false,
        historyApiFallback:true
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接