我正在使用React-router,当我点击链接按钮时,它工作得很好,但当我刷新我的网页时,它没有加载我想要的东西。

例如,我在localhost/joblist和一切都很好,因为我到达这里按下一个链接。但如果我刷新网页,我会得到:

Cannot GET /joblist

默认情况下,它不是这样工作的。最初我有我的URL localhost/#/和localhost/#/joblist,他们工作得很好。但我不喜欢这种类型的URL,所以试图删除#,我写道:

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);
});

这个问题不会发生在localhost/,这个总是返回我想要的。

这个应用程序是单页的,所以/joblist不需要向任何服务器询问任何事情。

我的整个路由器。

var routes = (
    <Route name="app" path="/" handler={App}>
        <Route name="joblist" path="/joblist" handler={JobList}/>
        <DefaultRoute handler={Dashboard}/>
        <NotFoundRoute handler={NotFound}/>
    </Route>
);

Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

当前回答

如果试图从IIS虚拟目录(不是网站的根目录)服务React应用程序:

在设置重定向时,'/'不会单独工作。对我来说,它也需要虚拟目录名。下面是我的网页配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <defaultDocument>
            <files>
                <remove value="default.aspx" />
                <remove value="iisstart.htm" />
                <remove value="index.htm" />
                <remove value="Default.asp" />
                <remove value="Default.htm" />
            </files>
        </defaultDocument>
        <rewrite>
            <rules>
                <rule name="React Routes" stopProcessing="true">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/YOURVIRTUALDIRECTORYNAME/" />
                </rule>
            </rules>
        </rewrite>
        <directoryBrowse enabled="false" />
        <httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
            <remove statusCode="500" subStatusCode="100" />
            <remove statusCode="500" subStatusCode="-1" />
            <remove statusCode="404" subStatusCode="-1" />
            <remove statusCode="403" subStatusCode="18" />
            <error statusCode="403" subStatusCode="18" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
            <error statusCode="404" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
            <error statusCode="500" prefixLanguageFilePath="" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
            <error statusCode="500" subStatusCode="100" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
        </httpErrors>
    </system.webServer>
</configuration>

除了网络。配置文件,React应用程序本身需要一些改变:

在文件包中。Json,你需要添加一个'主页'条目:

{
  "name": "sicon.react.crm",
  "version": "0.1.0",
  "private": true,
  "homepage": "/YOURVIRTUALDIRECTORYNAME/",
  "dependencies": {
...

我将basename添加到我的浏览器历史对象中,并将其传递给路由器以访问历史:

import  {createBrowserHistory } from 'history';

export default createBrowserHistory({
    //Pass the public URL as the base name for the router basename: process.env.PUBLIC_URL
});

我还在我的React路由器文件App.js中添加了这个属性:

<Router history={history} basename={process.env.PUBLIC_URL}>

最后,在index.html文件中,我在'title'标签上方添加了以下选项卡:

<base href="%PUBLIC_URL%/">

也许有些步骤是不需要的,但这似乎已经完成了我的工作。我不知道如何设置它运行在一个站点的根目录或虚拟目录,而不需要重新编译,作为包中的主页。据我所知,json不能在构建后交换。

其他回答

路由器可以用两种不同的方式调用,这取决于导航是发生在客户端还是服务器上。您已经将其配置为客户端操作。关键参数是run方法的第二个参数,即location。

当你使用React Router Link组件时,它会阻塞浏览器导航并调用transitionTo来做客户端导航。您正在使用HistoryLocation,因此它使用HTML5历史API通过在地址栏中模拟新URL来完成导航的错觉。如果您使用的是较旧的浏览器,这将不起作用。您将需要使用HashLocation组件。

When you hit refresh, you bypass all of the React and React Router code. The server gets the request for /joblist and it must return something. On the server you need to pass the path that was requested to the run method in order for it to render the correct view. You can use the same route map, but you'll probably need a different call to Router.run. As Charles points out, you can use URL rewriting to handle this. Another option is to use a Node.js server to handle all requests and pass the path value as the location argument.

例如,在Express.js中,它看起来是这样的:

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });
    });
});

请注意,正在传递请求路径以运行。为此,需要有一个服务器端视图引擎,可以将呈现的HTML传递给该引擎。在使用renderToString和在服务器上运行React时,还有许多其他注意事项。一旦页面在服务器上呈现,当你的应用程序在客户端加载时,它将再次呈现,并根据需要更新服务器端呈现的HTML。

使用useEffect读取URL并应用您想要的更改。


例如,一个页面有一个按钮,打开一个滑块。单击按钮时,将向URL中添加滑块的查询参数,并打开滑块。

问题-在刷新页面时打开滑块,加载的页面不显示打开的滑块,即使查询参数存在。

解决方案:添加一个useEffect,读取“slider”查询参数,并运行按钮处理程序。为滑块查询参数添加一个状态变量,并将其包含在useEffect的依赖数组中。

你不需要担心后端请求,假设在滑块组件中有一个。

我刚刚用Create React App做了一个网站,也遇到了同样的问题。

我使用react-router-dom包中的BrowserRouting。我在Nginx服务器上运行,添加以下到/etc/nginx/yourconfig.conf为我解决了这个问题:

location / {
  if (!-e $request_filename){
    rewrite ^(.*)$ /index.html break;
  }
}

如果你正在运行Apache,这对应于在.htaccess中添加以下内容:

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

这似乎也是Facebook自己提出的解决方案。

如果你在后端使用Express.js或其他框架,你可以添加如下所示的类似配置,并在配置中查看Webpack公共路径。如果你正在使用BrowserRouter,即使在重载时它也应该工作得很好。

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});

这里的答案都非常有用。配置我的Webpack服务器以期望路由对我有效。

devServer: {
   historyApiFallback: true,
   contentBase: './',
   hot: true
},

historyApiFallback为我修复了这个问题。现在路由工作正确,我可以刷新页面或直接输入URL。没有必要担心Node.js服务器上的变通方法。这个答案显然只适用于使用Webpack的情况。

查看我对React-router 2.0 browserHistory在刷新时不工作的回答,了解为什么需要刷新的更详细原因。