1023

我正在使用 React-router,当我单击链接按钮时它工作正常,但是当我刷新我的网页时它不会加载我想要的内容。

例如,我进去了localhost/joblist,一切都很好,因为我按链接到达这里。但是如果我刷新网页,我会得到:

Cannot GET /joblist

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

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

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

编辑:这个应用程序是单页的,所以/joblist不需要向任何服务器询问任何内容。

EDIT2:我的整个路由器。

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

53 回答 53

1568

查看对已接受答案的评论和这个问题的一般性质(“不工作”),我认为这可能是对这里涉及的问题进行一些一般性解释的好地方。因此,此答案旨在作为 OP 特定用例的背景信息/详细说明。请多多包涵。

服务器端与客户端

首先要了解的是,现在有 2 个地方可以解释 URL,而过去只有 1 个。过去,当生活很简单时,一些用户向http://example.com/about服务器发送请求,服务器检查 URL 的路径部分,确定用户正在请求 about 页面,然后发回该页面。

使用 React-Router 提供的客户端路由,事情就不那么简单了。起初,客户端还没有加载任何 JS 代码。所以第一个请求将永远是服务器。然后将返回一个页面,其中包含加载 React 和 React Router 等所需的脚本标签。只有当这些脚本加载后,阶段 2 才会开始。在第 2 阶段,例如,当用户单击“关于我们”导航链接时,URL仅在本地更改为http://example.com/about(由History API实现),但不会向服务器发出请求. 相反,React Router 在客户端做它的事情,决定渲染哪个 React 视图,然后渲染它。假设您的关于页面不需要进行任何 REST 调用,它已经完成了。您已从主页切换到关于我们,而没有触发任何服务器请求。

所以基本上当你点击一个链接时,一些 Javascript 会运行来操纵地址栏中的 URL,而不会导致页面刷新,这反过来会导致 React Router在客户端执行页面转换。

但是现在考虑如果您将 URL 复制粘贴到地址栏中并将其通过电子邮件发送给朋友会发生什么。您的朋友尚未加载您的网站。也就是说,她还处于第一阶段。她的机器上还没有运行 React Router。所以她的浏览器会http://example.com/about.

这就是你的麻烦开始的地方。到目前为止,您只需在服务器的 webroot 中放置一个静态 HTML 即可。但是,当从服务器请求时,这会给404所有其他 URL 带来错误。这些相同的 URL在客户端工作正常,因为 React Router 正在为你做路由,但它们在服务器端失败,除非你让你的服务器理解它们。

结合服务器端和客户端路由

如果您希望http://example.com/aboutURL 同时在服务器端和客户端工作,您需要在服务器端和客户端为其设置路由。有道理吗?

这就是您的选择开始的地方。解决方案的范围从完全绕过问题,通过返回引导 HTML 的包罗万象的路线,到服务器和客户端都运行相同 JS 代码的全面同构方法。

完全绕过问题:哈希历史

使用Hash History而不是Browser History,关于页面的 URL 将如下所示: http://example.com/#/about 哈希 ( #) 符号后面的部分不会发送到服务器。所以服务器只能http://example.com/按预期看到并发送索引页。React-Router 将拾取该#/about部分并显示正确的页面。

缺点

  • “丑陋”的网址
  • 使用这种方法无法进行服务器端渲染。就搜索引擎优化 (SEO) 而言,您的网站由一个页面组成,几乎没有任何内容。

包罗万象

/*使用这种方法,您确实使用了浏览器历史记录,但只是在发送到的服务器上设置了一个包罗万象的功能index.html,从而有效地为您提供与哈希历史记录大致相同的情况。但是,您确实有干净的 URL,您可以稍后改进此方案,而不必使所有用户的收藏夹无效。

缺点

  • 设置更复杂
  • 仍然没有好的SEO

杂交种

在混合方法中,您通过为特定路线添加特定脚本来扩展包罗万象的方案。您可以制作一些简单的 PHP 脚本来返回您网站中最重要的页面以及包含的内容,这样 Googlebot 至少可以看到您页面上的内容。

缺点

  • 设置更复杂
  • 只有那些你给予特殊待遇的路线才有好的 SEO
  • 复制用于在服务器和客户端上呈现内容的代码

同构

如果我们使用 Node JS 作为我们的服务器,那么我们可以在两端运行相同的JS 代码呢?现在,我们在一个 react-router 配置中定义了所有路由,我们不需要复制渲染代码。这就是所谓的“圣杯”。如果页面转换发生在客户端,服务器会发送与我们最终发送的完全相同的标记。该解决方案在 SEO 方面是最佳的。

缺点

  • 服务器必须(能够)运行 JS。我已经尝试过 Java icw Nashorn,但它对我不起作用。在实践中,这主要意味着您必须使用基于 Node JS 的服务器。
  • 许多棘手的环境问题(window在服务器端使用等)
  • 陡峭的学习曲线

我应该使用哪个?

选择一个你可以逃脱的。就个人而言,我认为包罗万象很容易设置,所以这将是我的最低要求。此设置允许您随着时间的推移改进事物。如果您已经在使用 Node JS 作为服务器平台,我肯定会研究做一个同构应用程序。是的,一开始很难,但一旦你掌握了窍门,它实际上是解决问题的一个非常优雅的解决方案。

所以基本上,对我来说,这将是决定因素。如果我的服务器在 Node JS 上运行,我会采用同构的;否则,我会选择包罗万象的解决方案,并随着时间的推移和 SEO 需求的需要对其进行扩展(混合解决方案)。

如果您想了解更多关于使用 React 进行同构(也称为“通用”)渲染的信息,这里有一些很好的教程:

另外,为了让您入门,我建议您查看一些入门工具包。选择一个与你的技术栈选择相匹配的技术栈(记住,React 只是 MVC 中的 V,你需要更多的东西来构建一个完整的应用程序)。首先查看 Facebook 自己发布的内容:

或者从社区中选择一个。现在有一个不错的网站试图索引所有这些:

我从这些开始:

目前,我正在使用受上述两个入门工具包启发的通用渲染的自制版本,但它们现在已经过时了。

祝你的任务好运!

于 2016-04-14T12:27:49.427 回答
175

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

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

historyApiFallback 为我解决了这个问题。现在路由工作正常,我可以刷新页面或直接输入 URL。无需担心节点服务器上的变通方法。这个答案显然只有在您使用 webpack 时才有效。

编辑:请参阅我的答案,了解为什么这是必要的更详细原因: https ://stackoverflow.com/a/37622953/5217568

于 2016-05-26T00:26:26.793 回答
173

您可以更改.htaccess文件并插入:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]
</IfModule>

我正在使用 这种方法是万能的,可能是让您入门的最简单方法react: "^16.12.0"react-router: "^5.1.2"

于 2016-11-14T15:10:58.703 回答
86

对于React Router V4用户:

如果您尝试通过其他答案中提到的哈希历史技术解决此问题,请注意

<Router history={hashHistory} >

在 V4 中不起作用,请HashRouter改用:

import { HashRouter } from 'react-router-dom'

<HashRouter>
  <App/>
</HashRouter>

参考:HashRouter

于 2017-04-18T11:16:23.123 回答
55

我刚刚使用 create-react-app 制作了一个网站,并且在这里遇到了同样的问题。我BrowserRoutingreact-router-dom包装中使用。我在 Nginx 服务器上运行,为我解决的问题是将以下内容添加到/etc/nginx/yourconfig.conf

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

这对应于.htaccess在运行 Appache 的情况下添加以下内容

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

这似乎也是 Facebook 自己建议的解决方案,可以在这里找到

于 2017-09-04T10:43:02.720 回答
38

在您的 index.htmlhead中,添加以下内容:

<base href="/">
<!-- This must come before the css and javascripts -->

然后在使用 webpack 开发服务器运行时使用此命令。

webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback

--history-api-fallback是重要的部分

于 2018-04-01T14:26:39.780 回答
33

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

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

当你点击刷新时,你绕过了所有的 React 和 React Router 代码。服务器收到请求/joblist,它必须返回一些东西。在服务器上,您需要将请求的路径传递给该run方法,以便它呈现正确的视图。您可以使用相同的路线图,但您可能需要对Router.run. 正如查尔斯指出的那样,您可以使用 URL 重写来处理这个问题。另一种选择是使用 node.js 服务器来处理所有请求并将路径值作为位置参数传递。

例如,在 express 中,它可能如下所示:

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

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

于 2015-01-21T20:37:03.487 回答
29

如果您使用的是 Create React App:

对于许多主要托管平台的解决方案,有一个很好的解决方案,您可以在 Create React App 页面上找到这里。例如,我将 React Router v4 和 Netlify 用于我的前端代码。只需将 1 个文件添加到我的公用文件夹(“_redirects”)和该文件中的一行代码:

/*  /index.html  200

现在,当我的网站进入浏览器或有人点击刷新时,我的网站会正确呈现诸如 mysite.com/pricing 之类的路径。

于 2017-06-09T22:01:37.203 回答
28

如果您通过 AWS 静态 S3 托管和 CloudFront 托管 React 应用程序

此问题由 CloudFront 以 403 Access Denied 消息响应而出现,因为它期望 /some/other/path 存在于我的 S3 文件夹中,但该路径仅存在于 React 使用 react-router 的路由内部。

解决方案是设置分发错误页面规则。转到 CloudFront 设置并选择您的分配。接下来转到“错误页面”选项卡。单击“创建自定义错误响应”并为 403 添加一个条目,因为这是我们得到的错误状态代码。将响应页面路径设置为 /index.html 并将状态代码设置为 200。最终结果以其简单性让我感到惊讶。提供索引页面,但 URL 保留在浏览器中,因此一旦 react 应用程序加载,它会检测 URL 路径并导航到所需的路由。

错误页面 403 规则

于 2017-11-23T03:10:45.160 回答
24

这可以解决你的问题

我在生产模式下的 ReactJS 应用程序中也遇到了同样的问题。这是问题的2解决方案。

1.将路由历史改为“hashHistory”而不是browserHistory代替

<Router history={hashHistory} >
   <Route path="/home" component={Home} />
   <Route path="/aboutus" component={AboutUs} />
</Router>

现在使用命令构建应用程序

sudo npm run build

然后将构建文件夹放在您的 var/www/ 文件夹中,现在应用程序可以正常工作,并在每个 URL 中添加 # 标记。喜欢

本地主机/#/home 本地主机/#/aboutus

解决方案2:没有#标签使用browserHistory,

在您的路由器中设置您的历史记录 = {browserHistory},现在使用 sudo npm run build 构建它。

你需要创建“conf”文件来解决404 not found页面,conf文件应该是这样的。

打开你的终端输入以下命令

cd /etc/apache2/sites-available ls nano sample.conf 在其中添加以下内容。

<VirtualHost *:80>
    ServerAdmin admin@0.0.0.0
    ServerName 0.0.0.0
    ServerAlias 0.0.0.0
    DocumentRoot /var/www/html/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/">
            Options Indexes FollowSymLinks
            AllowOverride all
            Require all granted
    </Directory>
</VirtualHost>

现在您需要使用以下命令启用 sample.conf 文件

cd /etc/apache2/sites-available
sudo a2ensite sample.conf

然后它会要求您重新加载 apache 服务器,使用 sudo service apache2 reload 或重新启动

然后打开您的 localhost/build 文件夹并添加具有以下内容的 .htaccess 文件。

   RewriteEngine On
   RewriteBase /
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_FILENAME} !-l
   RewriteRule ^.*$ / [L,QSA]

现在该应用程序正常运行。

注意:将 0.0.0.0 IP 更改为您的本地 IP 地址。

如果对此有任何疑问,请随时提出评论。

我希望它对其他人有帮助。

于 2017-02-07T15:55:00.280 回答
23

如果您在 IIS 上托管您的 React 应用程序,只需添加一个包含以下内容的 web.config 文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
</configuration>

这将告诉 IIS 服务器将主页返回给客户端,而不是 404 错误,并且不需要使用哈希历史记录。

于 2017-10-18T10:47:16.493 回答
18

Webpack 开发服务器有一个选项来启用它。打开package.json并添加--history-api-fallback. 这个解决方案对我有用。

反应路由器教程

于 2016-09-09T23:18:32.913 回答
17

将此添加到webpack.config.js

devServer: {
    historyApiFallback: true
}
于 2017-09-11T11:51:08.557 回答
15

生产堆栈:React、React Router v4、BrowswerRouter、Express、Nginx

1) 用户 BrowserRouter 用于漂亮的 url

// app.js

import { BrowserRouter as Router } from 'react-router-dom'

const App = () {
  render() {
    return (
        <Router>
           // your routes here
        </Router>
    )
  }
}

2)通过使用将 index.html 添加到所有未知请求/*

// server.js

app.get('/*', function(req, res) {   
  res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
    if (err) {
      res.status(500).send(err)
    }
  })
})

3) 将 webpack 与webpack -p

4)运行nodemon server.jsnode server.js

编辑:您可能希望让 nginx 在服务器块中处理此问题并忽略第 2 步:

location / {
    try_files $uri /index.html;
}
于 2017-08-09T17:46:53.670 回答
10

尝试使用以下代码在公用文件夹中添加“.htaccess”文件。

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ /index.html [L]  
于 2018-06-29T06:26:11.040 回答
8

如果您使用的是 firebase,您所要做的就是确保您在应用程序根目录下的 firebase.json 文件中有一个 rewrites 属性(在托管部分)。

例如:

{ 
  "hosting": {
    "rewrites": [{
      "source":"**",
      "destination": "/index.html"
    }]    
  }
}

希望这可以为其他人节省大量的挫败感和浪费的时间。

快乐的编码...

关于该主题的进一步阅读:

https://firebase.google.com/docs/hosting/full-config#rewrites

Firebase CLI:“配置为单页应用(将所有 url 重写为 /index.html)”

于 2018-07-24T02:46:49.527 回答
7

真的很棒的答案已经!但是,如果您使用 nginx 进行托管并且需要快速修复...将以下行添加到您的 nginx 配置中的 location 块内

location / {
  try_files $uri /index.html;
}
于 2021-03-18T08:40:46.253 回答
6

如果您确实有对 index.html 的后备,请确保在您的 index.html 文件中有以下内容:

<script>
  System.config({ baseURL: '/' });
</script>

这可能因项目而异。

于 2016-05-03T00:11:17.027 回答
5

对于那些使用 IIS 10 的人来说,这是您应该做的事情。确保您使用的是 browserHistory 。至于参考,我将给出路由的代码,但这并不重要,重要的是下面的组件代码之后的下一步:

class App extends Component {
    render() {
        return (
            <Router history={browserHistory}>
                <div>
                    <Root>
                        <Switch>
                            <Route exact path={"/"} component={Home} />    
                            <Route path={"/home"} component={Home} />
                            <Route path={"/createnewproject"} component={CreateNewProject} />
                            <Route path={"/projects"} component={Projects} />
                            <Route path="*" component={NotFoundRoute} />
                        </Switch>
                    </Root>
                </div>
            </Router>
        )
    }
}
render (<App />, window.document.getElementById("app"));

由于问题是 IIS 接收来自客户端浏览器的请求,它会将 URL 解释为请求页面,然后返回 404 页面,因为没有可用页面。请执行下列操作:

  1. 打开 IIS
  2. 展开服务器然后打开站点文件夹
  3. 单击网站/应用程序
  4. 转到错误页面
  5. 打开列表中的404错误状态项
  6. 而不是“将静态文件中的内容插入错误响应”选项,将其更改为“在此站点上执行 URL”并将“/”斜杠值添加到 URL。

现在它可以正常工作了。

在此处输入图像描述 在此处输入图像描述

我希望它有所帮助。:-)

于 2018-05-29T07:29:49.433 回答
5

如果您在后端使用 Express 或其他框架,您可以添加如下类似的配置并检查配置中的 Webpack 公共路径,如果您使用的是 BrowserRouter,即使重新加载它也应该可以正常工作

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});
于 2018-03-09T05:53:40.467 回答
4

如果您使用“create-react-app”命令,

要生成一个反应应用程序,那么 package.json 需要进行一次更改才能在浏览器中正确运行生产构建 React SPA。打开 package.json 并添加以下代码段,

"start": "webpack-dev-server --inline --content-base . --history-api-fallback"

这里最重要的部分是启用历史 API 回调的“--history-api-fallback”。

如果您使用 Spring 或任何其他后端 API,有时您会收到 404 错误。所以在这种情况下,您需要在后端有一个控制器来将任何请求(您想要的)转发到 index.html 文件以由 react-router 处理。下面演示使用 spring 编写的示例控制器。

@Controller
public class ForwardingController {
    @RequestMapping("/<any end point name>/{path:[^\\.]+}/**")
    public String forward(HttpServletRequest httpServletRequest) {
        return "forward:/";
    }
}

例如,如果我们将后端 API REST 端点作为“abc”(http://localhost:8080/abc/**),任何到达该端点的请求都会重定向到反应应用程序(index.html 文件),并且react-router 将处理该后记。

于 2020-01-30T09:43:31.400 回答
4

修复刷新或直接调用 URL 时出现的“无法获取 /URL”错误。

配置你的webpack.config.js以期望给定的链接像这样的路由。

module.exports = {
  entry: './app/index.js',
  output: {
       path: path.join(__dirname, '/bundle'),
       filename: 'index_bundle.js',
       publicPath: '/'
  },
于 2018-12-19T15:53:03.130 回答
4

我使用反应路由器(Apache)找到了我的 SPA 的解决方案。只需添加 .htaccess

<IfModule mod_rewrite.c>

  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]

</IfModule>

来源:https ://gist.github.com/alexsasharegan/173878f9d67055bfef63449fa7136042

于 2019-01-20T22:41:03.850 回答
4

您可以尝试阅读所有内容,尽管它不是我的:

https://www.andreasreiterer.at/fix-browserrouter-on-apache/

修复应用程序的路由现在这里是如何最终修复路由。要告诉 Apache 将请求重定向到我们的应用程序所在的 index.html,我们必须修改 .htaccess 文件。如果您的应用程序文件夹中还没有这样的文件,只需创建它。

然后确保你输入了那 4 行,它们会神奇地使你的路由工作。

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

在我们将该 .htaccess 文件放入与 index.html 相同的目录后,Apache 会将每个新请求直接重定向到您的应用程序。

奖励:将 React 应用程序部署到子目录

如果您将应用程序部署到子目录中,以便可以通过例如https://myapp.com/the-app访问它,您很快就会注意到还有另一个问题。每次单击新路由都会将 URL 转换为https://myapp.com/route-abc之类的内容——重新加载后会再次中断。但是有一个简单的解决方法:

BrowserRouter 有一个名为 basename 的属性,您可以在其中指定子目录路径:

从现在开始,每个像 /contacts 这样的路由都会产生一个像http://myapp.com/the-app/contacts这样的 URL 。

于 2019-10-14T04:19:48.520 回答
3

这个话题有点老了,已经解决了,但我想建议你一个简单、清晰和更好的解决方案。如果您使用 Web 服务器,它可以工作。

每个 Web 服务器都能够在 http 404 的情况下将用户重定向到错误页面。要解决此问题,您需要将用户重定向到索引页面。

如果您使用 Java 基础服务器(tomcat 或任何 Java 应用程序服务器),则解决方案可能如下:

网页.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- WELCOME FILE LIST -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- ERROR PAGES DEFINITION -->
    <error-page>
        <error-code>404</error-code>
        <location>/index.jsp</location>
    </error-page>

</web-app>

例子:

  • 获取http://example.com/about
  • Web服务器抛出http 404,因为该页面在服务器端不存在
  • 错误页面配置告诉服务器将 index.jsp 页面发送回用户
  • 然后 JS 将在客户端完成其余的工作,因为客户端的 url 仍然是http://example.com/about

就是这样,不再需要魔法:)

于 2017-01-15T14:01:24.753 回答
3

我还没有使用服务器端渲染,但我遇到了与 OP 相同的问题,其中 Link 似乎大部分时间都可以正常工作,但是当我有一个参数时却失败了。我将在这里记录我的解决方案,看看它是否对任何人有帮助。

我的主要 jsx 包含以下内容:

<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />

这适用于第一个匹配链接,但是当<Link>嵌套在该模型的详细信息页面上的表达式中的 :id 更改时,浏览器栏中的 url 会更改,但页面的内容最初并未更改以反映链接的模型。

问题是我使用props.params.id将模型设置在componentDidMount. 该组件仅安装一次,因此这意味着第一个模型是粘贴在页面上的模型,随后的链接更改了道具,但页面看起来没有变化。

将模型设置为组件状态componentDidMount和 in componentWillReceiveProps(它基于下一个 props)可以解决问题,并且页面内容会更改以反映所需的模型。

于 2016-09-12T06:28:02.650 回答
3

使用ReduxHashRouter也对我有用,只需简单地替换:

import {
  Router //replace Router
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <Router history={history}> //replace here saying Router
            <Layout/>
        </Router>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

至:

import {
  HashRouter //replaced with HashRouter
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <HashRouter history={history}> //replaced with HashRouter
            <Layout/>
        </HashRouter>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();
于 2020-03-24T14:09:14.303 回答
2

我通过更改 webpack.config.js 解决了这个问题。

我的新配置如下所示:

前 :

output: {
  path: path.join(__dirname, '/build/static/js'),
  filename: 'index.js'
},


devServer: {
  port: 3000
}

后 :

output: {
  path: path.join(__dirname, '/build/static/js'),
  filename: 'index.js',
  publicPath: '/'
},


devServer: {
  historyApiFallback: true,
  port: 3000
}
于 2020-04-29T18:41:03.853 回答
2

万一有人在这里用 Laravel 寻找 React JS SPA 的解决方案。 公认的答案是对为什么会发生此类问题的最佳解释。如前所述,您必须同时配置客户端和服务器端。在你的刀片模板中,包含 js 捆绑文件,确保URL facade像这样使用

<script src="{{ URL::to('js/user/spa.js') }}"></script>

在您的路线中,确保将其添加到刀片模板所在的主端点。例如,

Route::get('/setting-alerts', function () {
   return view('user.set-alerts');
});

以上是刀片模板的主要端点。现在也添加一条可选路线,

Route::get('/setting-alerts/{spa?}', function () {
  return view('user.set-alerts');
});

发生的问题是首先加载刀片模板,然后加载反应路由器。因此,当您加载时'/setting-alerts',它会加载 html 和 js。但是当你加载时'/setting-alerts/about',它首先在服务器端加载。由于在服务器端,此位置没有任何内容,因此返回未找到。当你有那个可选的路由器时,它会加载相同的页面并且反应路由器也会被加载,然后反应加载器决定显示哪个组件。希望这可以帮助。

于 2017-11-14T03:23:55.810 回答
2

为Joshua Dyck 的回答添加更多信息。

如果您正在使用 Firebase 并希望同时使用根路由和子目录路由,则需要在您的 中添加以下代码firebase.json

{
  "hosting": {
    "rewrites": [
      {
        "source": "*",
        "destination": "/index.html"
      },
      {
        "source": "/subdirectory/**",
        "destination": "/subdirectory/index.html"
      }
    ]
  }
}

例子:

您正在为客户建立网站。您希望网站所有者在https://your.domain.com/management中添加信息,而网站用户将导航到https://your.domain.com

在这种情况下,您的firebase.json文件将如下所示:

{
  "hosting": {
    "rewrites": [
      {
        "source": "*",
        "destination": "/index.html"
      },
      {
        "source": "/management/**",
        "destination": "/management/index.html"
      }
    ]
  }
}
于 2019-03-12T07:01:14.893 回答
2

我们使用快递的404处理方式

// path to the static react build directory    
const frontend = path.join(__dirname, 'react-app/build');

// map the requests to the static react build directory
app.use('/', express.static(frontend));

// all the unknown requests are redirected to the react SPA
app.use(function (req, res, next) {
    res.sendFile(path.join(frontend, 'index.html'));
});

奇迹般有效。现场演示是我们的网站

于 2019-09-22T13:07:17.360 回答
2

如果您在 IIS 中托管;将此添加到我的 webconfig 解决了我的问题

<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
    <remove statusCode="500" subStatusCode="100" />
    <remove statusCode="500" subStatusCode="-1" />
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" path="/" responseMode="ExecuteURL" />
    <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
    <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
</httpErrors>

您可以为任何其他服务器进行类似的配置

于 2017-04-26T08:32:50.537 回答
2

对于那些因为试图从 IIS 虚拟目录(而不是网站的根目录)提供反应应用程序而来到这里的人,那么这可能适合你。

设置重定向时,'/' 不能单独工作,对我来说,它也需要其中的虚拟目录名称。这是我的网络配置的样子:

<?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>

除了 web.config 文件之外,react 应用程序本身还需要一些更改:

在 package.json 你需要添加一个“主页”条目:

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

我将基本名称添加到我的浏览器历史记录对象中,我将其传递给路由器以访问历史记录:

import  {createBrowserHistory } from 'history';

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

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

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

最后,在 index.html 中,我在“title”标签上方添加了以下标签

  <base href="%PUBLIC_URL%/">

可能是需要注意的一些步骤,但这似乎已经为我完成了这项工作。我不知道如何将其设置为在站点的根目录或虚拟目录中运行而无需重新编译,尽管package.json据我所知,在构建后无法交换主页。

于 2020-07-19T20:32:13.487 回答
1

当我将 React 用于前端和react-router-dom路由时,我在 Electron 中遇到了这个问题。我替换BrowserRouterHashRouter它并修复了。这是一个简单的例子

import {
  HashRouter as Router,
  Switch,
  Route,
} from "react-router-dom";
于 2021-06-03T12:56:50.273 回答
1

在我的情况下,当我在其中使用参数时,没有加载 url。

作为快速修复,我 <base href="<yourdomain/IP>"></base> 在 build 文件夹中的 index.html 文件的标签下添加。

这只是解决了我的问题。

于 2021-02-02T05:41:26.050 回答
1

当我使用 .Net Core MVC 时,这样的事情帮助了我:

    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var url = Request.Path + Request.QueryString;
            return App(url);
        }

        [Route("App")]
        public IActionResult App(string url)
        {
            return View("/wwwroot/app/build/index.html");
        }
   }

基本上在 MVC 方面,所有不匹配的路由都将落入Home/Index. startup.cs在内部Index可以获取原始请求 url 并将其传递到任何需要的地方。

启动.cs

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });
于 2019-03-11T04:41:36.427 回答
1

我正在使用 WebPack,我遇到了同样的问题解决方案 => 在您的 server.js 文件中

const express = require('express');
const app = express();

app.use(express.static(path.resolve(__dirname, '../dist')));
  app.get('*', function (req, res) {
    res.sendFile(path.resolve(__dirname, '../dist/index.html'));
    // res.end();
  });

为什么我的应用程序在刷新后不呈现?

于 2019-02-19T12:02:10.843 回答
1

如果您在 Google 存储桶上运行它,简单的解决方案是考虑错误(404 未找到)页面的“index.html”。

为此:

  1. 在存储桶列表中,找到您创建的存储桶。
  2. 单击与存储桶关联的存储桶溢出菜单 (...),然后选择编辑网站配置。
  3. 在网站配置对话框中,也将主页指定为错误页面。
于 2020-07-16T21:28:13.437 回答
1

我正在使用ASP.NET CoreReact。生产环境中手动路由和刷新路由问题的解决方案是在ASP.NET Core主项目web.config的根目录下创建文件,该文件将在生产服务器上配置路由。

项目中文件的位置:

在此处输入图像描述

文件内容web.config

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="Rewrite Text Requests" stopProcessing="true">
                    <match url=".*" />
                    <conditions>
                        <add input="{HTTP_METHOD}" pattern="^GET$" />
                        <add input="{HTTP_ACCEPT}" pattern="^text/html" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/index.html" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>
于 2021-10-14T12:28:05.460 回答
1

如果您来这里并且使用的是 apache 并且没有 .htaccess 文件,那么这是一个对我有用的配置文件:

sites-enabled/somedomain.com.conf

<VirtualHost *:80>
    ServerName somedomain.com
    ServerAlias *.somedomain.com
    DocumentRoot /www/somedomain.com/build

    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /www/somedomain.com/build/index.html [L,NC,QSA]

</VirtualHost>
于 2019-07-16T23:54:24.243 回答
1

使用 preact-router 解决 Preact

适用于刷新和直接访问

对于那些通过 Google 发现这一点的人,这里有一个 preact-router + 哈希历史的演示:

const { h, Component, render } = preact; /** @jsx h */
const { Router } = preactRouter;
const { createHashHistory } = History;
const App = () => (
    <div>
        <AddressBar />

        <Router history={createHashHistory()}>
            <div path="/">
                <p>
                    all paths in preact-router are still /normal/urls.
                    using hash history rewrites them to /#/hash/urls
                </p>
                Example: <a href="/page2">page 2</a>
            </div>
            <div path="/page2">
                <p>Page Two</p>
                <a href="/">back to home</a><br/>
            </div>
        </Router>
    </div>
);

jsfiddle

于 2017-10-05T12:08:33.670 回答
1

在后端使用 express 并在前端使用 React(没有 react-create-app)和reach/router,显示正确的reach/router route react 组件,并且在地址栏中按回车时菜单链接设置为活动样式例如 http://localhost:8050/pages。请在下面结帐,或直接到我的仓库https://github.com/nickjohngray/staticbackeditor,所有代码都在那里。

网页包

设置代理。这允许来自端口 3000(React)的任何调用调用服务器,包括在按下回车键时调用 get index.html 或地址栏中的任何内容。它还允许调用 API 路由,以获取 JSON 数据

比如 await axios.post('/api/login', {email, pwd})

devServer: {
    port: 3000,
    open: true,
    proxy: {
      '/': 'http://localhost:8050',
    }
  }

设置快速路线

app.get('*', (req, res) => {
    console.log('sending index.html')
    res.sendFile(path.resolve('dist', 'index.html'))

});

这将匹配来自 react 的任何请求,它只返回 index.html 页面,该页面在我的 dist 文件夹中,此页面当然有一个更单页的 react 应用程序。(注意任何其他路线都应该出现在这上面,在我的情况下,这些是我的 API 路线)

反应路线

<Router>
    <Home path="/" />
    <Pages path="pages"/>
    <ErrorPage path="error"/>
    <Products path="products"/>
    <NotFound default />
</Router>

这些路由在我的 Layout 组件中定义,当路径匹配时将加载相应的组件。

反应布局构造函数

 constructor(props) {
        super(props);


        this.props.changeURL({URL: globalHistory.location.pathname});
}

Layout 构造函数在加载后立即被调用。在这里,我调用我的菜单监听的 redux 操作 changeURL,以便它可以突出显示正确的菜单项,如下所示:

菜单代码

<nav>
    {this.state.links.map( (link) =>
    <Link className={this.getActiveLinkClassName(link.path) } to={link.path}> 
      {link.name}
    </Link>)}            
</nav>
于 2020-08-08T14:17:35.820 回答
0

我正在使用 .Net Core 3.1 并添加了扩展MapFallbackToController

启动.cs

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");

        endpoints.MapFallbackToController("Index", "Home");
    });
于 2020-05-08T15:38:32.340 回答
0

我喜欢这种处理方式。尝试 在服务器端添加: yourSPAPageRoute/*以解决此问题。

我采用了这种方法,因为即使是原生 HTML5 History API 也不支持页面刷新时的正确重定向(据我所知)。

注意:选定的答案已经解决了这个问题,但我试图更具体。

特快路线

测试 - 历史 API 经过测试,只想分享这个。

希望能帮助到你。

于 2018-12-05T19:17:50.813 回答
0

Here is a frontend workaround I discovered that does not require modifying anything on the server.

Let's say your site is mysite.com and you have a React Route to mysite.com/about. In index.js, where you mount your top-level component, you can put another Router like:

ReactDOM.render(
<Router>
    <div>
        <Route exact path="/" component={Home} />
        <Route exact path="/about"
            render={(props) => <Home {...props} refreshRout={"/about"}/>}
        />
    </div>
</Router>,

I'm assuming you have the original Router located somewhere below the top-level component in the virtual DOM. You also have to catch the url in your .urls if you are using Django like:

urlpatterns = [
       path('about/', views.index),
]

This will depend on what backend you're using, however. Requesting mysite/about will get you into index.js (where you mount the top-level component) where you can use the render prop of the Route, rather than the component prop, and pass '/about' as a prop to, in this example, the Home component.

Within Home, in either componentDidMount() or the useEffect() hook, do:

useEffect() {   
   //check that this.props.refreshRoute actually exists before executing the 
   //following line    
   this.props.history.replace(this.props.refreshRoute);
}

I've assumed your Home component is rendering something like:

<Router>
   <Route exact path="/" component={SomeComponent} />
   <Route path="/about" component={AboutComponent} />
</Router>

Credit to (Pass props to a component rendered by React Router) for how to pass props to components in Routes.

于 2019-07-28T21:44:13.047 回答
0

即使您立即指向 url,请求数据的另一种方法是让每个组件都有一个方法来调用最后一个参数,例如 /about/ test,然后到您的状态提供者,您拥有连接到您想要的组件的函数请求数据

于 2020-06-04T20:43:00.820 回答
0

我正在使用 React.js + Webpack 模式。我在文件中添加了--history-api-fallback参数。package.json然后页面刷新工作正常。每次更改代码时,网页都会自动刷新。

"scripts": {
  "start": "rimraf build && cross-env NODE_ENV='development' webpack --mode development && cross-env NODE_ENV=development webpack-dev-server --history-api-fallback",
  ...
}
于 2020-08-12T21:51:16.343 回答
0

我有同样的问题,这个解决方案对我们有用..

背景:

我们在同一台服务器上托管多个应用程序。当我们刷新服务器时,服务器将不知道在 dist 文件夹中为该特定应用程序查找索引的位置。上面的链接将带您了解对我们有用的内容...希望这会有所帮助,因为我们已经花费了相当多的时间来找出满足我们需求的解决方案。

我们正在使用:

package.json

"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"
}

我的 webpack.config.js

webpack.config.js

/* eslint-disable */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const babelPolyfill = require('babel-polyfill');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
  template: __dirname + '/app/views/index.html',
  filename: 'index.html',
  inject: 'body'
});

module.exports = {
  entry: [
    'babel-polyfill', './app/index.js'
  ],
  output: {
    path: __dirname + '/dist/your_app_name_here',
    filename: 'index_bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'babel-loader',
      query : {
          presets : ["env", "react", "stage-1"]
      },
      exclude: /node_modules/
    }]
  },
  plugins: [HTMLWebpackPluginConfig]
}

我的 index.js

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import Routes from './Routes'
import { Provider } from 'react-redux'
import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import configureStore from './store/configureStore'
import { syncHistoryWithStore } from 'react-router-redux'
import { persistStore } from 'redux-persist'

const store = configureStore();

const browserHistory = useRouterHistory(createHistory) ({
  basename: '/your_app_name_here'
})
const history = syncHistoryWithStore(browserHistory, store)

persistStore(store, {blacklist: ['routing']}, () => {
  console.log('rehydration complete')
})
// persistStore(store).purge()


ReactDOM.render(
    <Provider store={store}>
      <div>
        <Routes history={history} />
      </div>
    </Provider>,
  document.getElementById('mount')
)

我的 app.js

var express = require('express');
var app = express();

app.use(express.static(__dirname + '/dist'));
// app.use(express.static(__dirname + '/app/assets'));
app.set('views', __dirname + '/dist/your_app_name_here');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

app.get('/*', function (req, res) {
    res.render('index');
});

app.listen(8081, function () {
  console.log('MD listening on port 8081!');
});
于 2017-05-05T15:39:41.393 回答
0

假设您有以下 Home 路由定义

<Route exact path="/" render={routeProps => (
   <Home routeProps={routeProps}/>
)}/>

{/*optional catch-all router */}
<Route render={routeProps => (
       <div><h4>404 not found</h4></div>
)}/>

在您的 Home 组件中,您可以在 ComponentWillMount 事件中拦截请求,

const searchPath = this.props.routeProps.location.search;

if (searchPath){
    this.props.routeProps.history.push("/" + searchPath.replace("?",""));
}
else{
    /*.... originally Home event */
}

现在,您可以请求 /?joblist,而不是在 url 调用 /joblist,组件会自动将请求重定向到 /joblist(注意路径中的额外问号)

于 2019-07-09T05:43:54.760 回答
0

我知道这个问题已经被回答到死了,但它并没有解决你想使用带有代理通行证的浏览器路由器的问题,你不能使用 root。

对我来说,解决方案非常简单。

假设您有一个指向某个端口的 url。

location / {
  proxy_pass http://127.0.0.1:30002/;
  proxy_set_header    Host            $host;
  port_in_redirect    off;
}

现在由于浏览器路由器子路径已损坏。但是,您知道子路径是什么。

解决这个问题?对于子路径/contact

# just copy paste.
location /contact/ {
  proxy_pass http://127.0.0.1:30002/;
  proxy_set_header    Host            $host;
}

我没有尝试过其他任何方法,但这个简单的修复方法就像一个该死的魅力。

于 2020-11-09T14:08:25.940 回答
-1

HashRouter 将是一个简单的实现,

import {HashRouter as Router,Switch,Route,Link} from 'react-router-dom';


  function App() {
  return (
    <Router>
        <Switch>
          <Route path="/" exact component={InitialComponent} />
          <Route path="/some" exact component={SomeOtherComponent} />
        </Switch>
      </Router>
  );
}

在浏览器中会是这样的 - http:localhost:3000/#/http:localhost:3000/#/some

于 2021-07-10T23:26:32.020 回答
-1

您可以将 Vercel 的托管用于您的 react 应用程序,并在您的 react 应用程序中使用相同的旧路由方式使用 BrowserRouting 。

您需要在项目的根目录中添加一个 vercel.json 文件并将此代码添加到其中:

{
  "rewrites": [
    {
      "source": "/((?!api/.*).*)",
      "destination": "/index.html"
    }
  ]
}

这工作得很好,希望它有所帮助。

于 2021-09-22T20:21:10.470 回答
-2

当您在刷新 dom 组件后出现无法获得 403 错误时,它非常简单。只需在你的 webpack 配置中添加这一行,'historyApiFallback: true'。这节省了我一整天。

于 2018-01-02T12:53:24.163 回答