【问题标题】:Reload the page gets 404 error using React Router使用 React Router 重新加载页面时出现 404 错误
【发布时间】:2018-10-21 07:51:14
【问题描述】:

我有……

后端

Node.js/express 服务器在对某些路由发出请求时提供文件。

前端

React 页面调用后端请求数据以填充页面。

我在前端使用 react-router v4。每当我导航到不在确切路径的路线并重新加载页面时,我都会收到 404 错误。我明白为什么这不起作用;浏览器向 react-router 处理的路径发出请求,由于它在服务器上找不到该路由,所以我得到 404。

我正在寻找解决这个问题的方法。

// BrowserRouter imported as Router
<Router>
   <Route exact path='/main' component={Main} />
   <Route path='/sub1' component={SubOne} />
   <Route path='/sub2' component={SubTwo} />
</Router>

当我在浏览器中转到/main 时,会呈现&lt;Main /&gt;。假设在&lt;Main /&gt; 内部,分别有指向/sub1/sub2 的链接。我点击/sub2。组件和页面内容呈现不会失败。然后我刷新页面,无论是偶然还是有意(比如组件 Sub2 将状态提升到 Main)。

如何避免刷新后出现 404?如果我使用 React-Router,如何在刷新后获取“我所在”的页面/组件?

【问题讨论】:

  • 我相信这与您的应用程序没有在服务器端呈现有关。如果您使用create-react-app cli,它会自动为您执行此操作。但是,如果您使用自己的样板,则需要使用 webpack 来处理 404 回退。
  • 正确;我没有使用 create-react-app,不幸的是我现在无法切换到它。我必须为当前的设置设计一个解决方案...你如何设置 webpack 来处理 404 回退
  • 确保在 webpack 输出中指定了 publicPath: '/'

标签: javascript reactjs react-router


【解决方案1】:

只需将以下“函数”添加到您的服务器应用程序的 app.js 文件中:

app.get('*', (req, res) => {
   res.sendFile(path.join(__dirname+'/client/build/index.html'));
});

将“client”替换为您的客户端应用程序目录的名称。对于与您定义的请求不匹配的任何请求,这将充当“接球球”。它对我有用。

更多信息请观看以下教程:https://www.youtube.com/watch?v=xwovj4-eM3Y

【讨论】:

    【解决方案2】:

    我的解决方案是使用 content-history-api-fallback 模块

    首先:从 'connect-history-api-fallback' 导入历史记录

    然后:

    app.use(history({
        rewrites:[
            {from: /^\/api\/.*$/, to: function(context){
                return context.parsedUrl.pathname;
            }},
            {from: /\/.*/, to: '/'}
        ]
    }))
    
    app.get('/', function(req, res, next){
        res.render('index');
    })
    
    app.use('/api', api);
    

    【讨论】:

      【解决方案3】:

      你得到 404 的原因是刷新页面会导致网络往返到达 服务器,而 react-router 是一个客户端路由库,它不是处理服务器端路由。

      当用户点击刷新按钮或直接访问着陆页以外的页面时,例如/help 或 /help/online,因为 Web 服务器会绕过索引文件以在此位置定位文件。由于您的应用程序是 SPA,因此 Web 服务器将无法尝试检索文件并向用户返回 404 - Not Found 消息。

      由于您使用的是 Express 服务器,connect-history-api-fallback(Express 中间件)可用于通过您的索引页面代理所有收到的请求(包括未知请求,在您的情况下为 /sub2)。然后重新启动您的 SPA 并通过 react router 路由到客户端上的 /sub2。

      如果你是使用 webpack-dev-server 进行本地开发,那么就像打开devServer.historyApiFallback 一样简单。

      【讨论】:

        【解决方案4】:

        大约 2 个月前,我遇到了与您相同的问题。我对使用 React 进行服务器端渲染知之甚少。我对它的一般概念感到困惑。但是,我不想使用 create-react-app cli。我想使用我自己的样板。经过研究,我发现我必须配置我的 webpack 来处理我的 404 重新加载后备。

        这是我当前的 webpack 设置:

        请注意,如果您使用的是 v4,请只注意historyApiFallback: true,它允许您刷新页面而不会引发 404 错误。另外,我忘了说这需要 webpack-dev-server 才能工作。

        const webpack = require('webpack');
        const path = require('path');
        const nodeExternals = require('webpack-node-externals');
        const HtmlWebPackPlugin = require('html-webpack-plugin');
        
        var browserConfig = {
            devServer: {
                historyApiFallback: true,
                proxy: {
                    "/api": "http://localhost:3012"
                }
            },
            entry: ['babel-polyfill', __dirname + '/src/index.js'],
            output: {
                path: path.resolve(__dirname + '/public'),
                filename: 'bundle.js',
                publicPath: '/'
            },
            module: {
                rules: [
                    {
                        test: /\.jsx?$/,
                        exclude: /node_modules/,
                        use: {
                            loader: 'babel-loader',
                            query: {
                                presets: ['react', 'env', 'stage-0']
                            }
                        }
                    }
                ]
            },
            plugins: [
                new HtmlWebPackPlugin({
                    template: './public/index.html',
                })
            ]
        }
        
        var serverConfig = {
            target: 'node',
            externals: [nodeExternals()],
            entry: __dirname + '/server/main.js',
            output: {
                path: path.resolve(__dirname + '/public'),
                filename: 'server.js',
                publicPath: '/'
            },
            module: {
                rules: [
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        use: {
                            loader: 'babel-loader',
                            query: {
                                presets: ['react', 'env', 'stage-0']
                            }
                        }
                    }
                ]
            }
        }
        
        module.exports = [browserConfig, serverConfig]
        

        【讨论】:

        • 你能再解释一下吗?我尝试将 config.devServer.historyApiFallback = true 添加到我的 webpack 但它没有这样做,谢谢
        【解决方案5】:

        使用“重定向”指令将子路径映射到实际路径

        https://reacttraining.com/react-router/web/example/no-match

        【讨论】:

          最近更新 更多