使用React Router重新加载页面出现404错误

8

我有一个后端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 后,会渲染出 <Main />。假设在 <Main /> 中有分别链接到 /sub1/sub2 的链接。我点击了 /sub2。组件和页面内容都能够成功渲染。然后,我刷新了页面,无论是意外还是故意(例如:组件 Sub2 将状态提升至 Main)。

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


我认为这与您的应用程序没有进行服务器端渲染有关。如果您使用了 create-react-app cli,它会自动为您完成此操作。但是,如果您使用自己的样板文件,则需要使用 webpack 来处理 404 回退。 - Curious13
正确;我没有使用create-react-app,而且很遗憾我现在不能切换到它。我必须为当前的设置设计一个解决方案... 你如何设置webpack来处理404回退 - jsdev17
1
请确保在您的webpack输出中指定了publicPath: '/' - Dennis
5个回答

5
我大约两个月前遇到了和你一样的问题。我对React的服务器端渲染知识非常少,对其概念感到困惑。然而,我不想使用create-react-app cli,我想使用自己的脚手架。在做研究时,我发现我必须配置我的webpack来处理404重载回退。
这是我的当前webpack设置:
请注意,只需关注允许您在使用v4时刷新页面而不会抛出404错误的“historyApiFallback: true”。此外,我忘记提到这需要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]

1
您能再详细解释一下吗?我尝试在Webpack中添加config.devServer.historyApiFallback = true,但它似乎不起作用。谢谢。 - user3808307

2

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

首先:从 'connect-history-api-fallback' 导入 history。

然后:

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);

2
您之所以会得到404错误是因为刷新页面需要通过网络往返来访问服务器,而react-router是一个客户端路由库,它无法处理服务器端路由。

当用户点击刷新按钮或者直接访问其他页面时(如/help或/help/online),Web服务器将绕过索引文件并定位到该位置的文件。由于您的应用程序是单页应用程序(SPA),因此Web服务器将尝试检索该文件而失败,并向用户返回404-未找到消息。

由于您正在使用Express服务器,可以使用connect-history-api-fallback(Express中间件)代理所有接收到的请求(包括未知请求,例如/sub2)通过您的index页面。然后重启SPA并使用react router在客户端路由到/sub2。

如果您正在使用webpack-dev-server进行本地开发,则只需打开devServer.historyApiFallback即可。


0
只需将以下“function”添加到您的服务器应用程序的app.js文件中即可:
app.get('*', (req, res) => {
   res.sendFile(path.join(__dirname+'/client/build/index.html'));
});

"client"替换为您的客户端应用程序目录的名称。这将作为任何不符合您定义的请求的“抛接球”。它对我很有效。

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


0

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