手动刷新或写入时,React-router url 不起作用

Posted

技术标签:

【中文标题】手动刷新或写入时,React-router url 不起作用【英文标题】:React-router urls don't work when refreshing or writing manually 【发布时间】:2021-05-16 12:02:37 【问题描述】:

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

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

Cannot GET /joblist

默认情况下,它不是这样工作的。最初,我的 URL 为 localhost/#/localhost/#/joblist,它们运行良好。但我不喜欢这种网址,所以想抹掉那个#,我写道:

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

【问题讨论】:

除非您使用 htaccess 加载您的主要游览页面并告诉您的路由器使用 location.pathname 它不会工作.. 你是如何删除# 符号的?谢谢! 如果您将 React 应用程序托管在 S3 存储桶中,您只需将错误文档设置为 index.html。这将确保index.html 无论如何都会被击中。 在我的情况下,它在 windows 中可以正常工作,但在 linux 中却不行 这是帮助解决我的问题的参考:github.com/facebook/create-react-app/blob/master/packages/… 【参考方案1】:

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

当您使用 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。

【讨论】:

excuse 对不起,请您解释一下下一条语句“当您点击刷新时,您会绕过所有 React 和 React Router 代码”?为什么会这样? React 路由器通常仅用于处理浏览器中的不同“路径”。有两种典型的方法可以做到这一点:旧式哈希路径和新式历史 API。浏览器刷新将发出服务器请求,这将绕过您的客户端反应路由器代码。您将需要处理服务器上的路径(但仅当您使用历史 API 时)。您可以为此使用反应路由器,但您需要执行类似于我上面描述的操作。 知道了。但是如果服务器使用非 JS 语言(比如说 Java)怎么办? 对不起……你的意思是你需要一个快速服务器来呈现一个“非根”的路由?我首先感到惊讶的是,同构的定义并未在其概念中包含数据获取(如果您想在数据服务器端渲染视图,事情会非常复杂,尽管这将是一个明显的 SEO 需求——并且同构声称它)。现在我就像“WTF 反应”,说真的……如果我错了,请纠正我,ember/angular/backbone 是否需要服务器来渲染路由?我真的不明白这种使用路由的臃肿要求 如果我的 react 应用程序不是同构的,是否意味着 react-router 中的刷新不起作用?【参考方案2】:

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

服务器端与客户端

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

使用 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 即可。但这会给所有其他 URL 带来404 错误当从服务器请求时。这些相同的 URL在客户端工作正常,因为 React Router 正在为你做路由,但它们在服务器端失败 除非你让你的服务器理解他们。

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

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

这是您选择的开始。解决方案的范围从完全绕过问题,通过返回引导 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 i.c.w。 Nashorn,但它对我不起作用。在实践中,这主要意味着您必须使用基于 Node JS 的服务器。 许多棘手的环境问题(在服务器端使用 window 等) 陡峭的学习曲线

.

我应该使用哪个?

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

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

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

React to the future with isomorphic apps The Pain and the Joy of creating isomorphic apps in ReactJS How to Implement Node + React Isomorphic JavaScript & Why it Matters

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

Create React App

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

Pick your perfect React starter project

我从这些开始:

React Isomorphic Starterkit React Redux Universal Hot Example

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

祝你任务顺利!

【讨论】:

很棒的 Stijn 帖子!你会推荐使用同构反应应用程序的入门工具包吗?如果是这样,你能举一个你喜欢的例子吗? @Paulos3000 这取决于您使用的服务器。基本上,您为/* 定义一个路由,并使其响应您的HTML 页面。这里的棘手之处在于确保您不会使用此路由拦截对 .js 和 .css 文件的请求。 @Paulos3000 相关问题请参见此处:for Apache/php、for Express/js、for J2E/Java。 @Stijn de Witt 这是一个很好的澄清,并在这个主题上得到了很好的回答。但是,当您的应用程序可以在子目录中提供时(例如通过 iis),有哪些选项。即:domain/appName/routeName 或 domain/appName/subfolder/routeName @LeonGaban 似乎自从写了这个答案以来,React Router 改变了他们的实现。他们现在有不同的路由器实例用于不同的历史记录,并且他们在后台配置历史记录。基本原理还是一样的。【参考方案3】:

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

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

这可能因项目而异。

【讨论】:

将此添加到您的 html head: 【参考方案4】:

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

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

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

编辑:请在此处查看我的答案,了解为什么这是必要的更详细的原因: https://***.com/a/37622953/5217568

【讨论】:

请注意,Webpack 团队 recommends against 在生产中使用开发服务器。 对于通用开发目的,这是最好的解决方案。 historyApiFallback 就足够了。至于所有其他选项,也可以从 CLI 使用标志 --history-api-fallback 进行设置。 @Kunok 它没有。这是开发的快速解决方案,但您仍然需要为生产解决问题。 contentBase :':/' 因为你的应用文件可以从 url 访问 适用于 React 17。谢谢【参考方案5】:

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

react-router-tutorial

【讨论】:

webpack-dev-server 没问题,但我如何为生产版本解决这个问题?【参考方案6】:

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

我的主要 jsx 包含这个:

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

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

问题是我使用props.params.id 将模型设置为componentDidMount。该组件仅安装一次,因此这意味着第一个模型是粘贴在页面上的模型,随后的链接会更改道具但保持页面外观不变。

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

【讨论】:

最好使用组件构造函数(也可以访问props)i.s.o。 componentDidMount 如果您想尝试服务器端渲染。因为componentDidMount 只在浏览器中被调用。它的目的是用 DOM 做一些事情,例如将事件侦听器附加到 body 等你在 render 中无法做到的事情。【参考方案7】:

您可以更改您的 .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" 这种方法是万能的,可能是帮助您入门的最简单方法。

【讨论】:

这很好用!如果您不想重组应用程序,最简单的方法 别忘了 RewriteEngine On 作为第一行 请注意,此答案是指 Apache 服务器上的配置。这不是 React 应用程序的一部分。 谁能帮帮我?这个htaccess放在哪里?添加文件后需要npm run build吗? @TayyabFerozi 您必须确保RewriteBase 和最后一个RewriteRule 之后的路径与应用程序所在的文件夹匹配(如果它是子文件夹)【参考方案8】:

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

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

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

web.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。

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

【讨论】:

这太棒了!我正在使用 Wildfly 10.1 服务器并对我的 web.xml 文件进行了此更新,但我将位置设置为“/”。这是因为在我的反应代码中,我使用了这样的浏览器历史记录:const browserHistory = useRouterHistory(createHistory)( basename: '/&lt;appname&gt;' ); 您确定这不会影响 SEO 吗?您正在为实际存在的页面提供 404 状态。用户可能永远不会意识到这一点,但机器人确实会非常关注,事实上非常关注,以至于它们不会抓取您的页面。【参考方案9】:

这可以解决你的问题

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

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

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

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

sudo npm run build

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

本地主机/#/home 本地主机/#/关于我们

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

在你的路由器中设置你的 history = browserHistory,现在使用 sudo npm run build 构建它。

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

打开终端输入以下命令

cd /etc/apache2/sites-available ls 纳米样本.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 重新加载或重启

然后打开您的 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 地址。

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

希望对其他人有所帮助。

【讨论】:

第一个解决方案是否适用于"react-router-dom": "^5.1.2"?,我正在使用&lt;BrowserRouter&gt; .htaccess 为我修复了它。 在哪里,我将在解决方案 2 中获取 browserHistory。 在为我修复的构建文件夹中仅添加 .htaccess 文件,谢谢 为什么会有人sudo build【参考方案10】:

对于 React Router V4 用户:

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

<Router history=hashHistory >

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

import  HashRouter  from 'react-router-dom'

<HashRouter>
  <App/>
</HashRouter>

参考:HashRouter

【讨论】:

您能否详细说明 HashRouter 解决此问题的原因?您提供的链接没有为我解释。另外,有没有办法隐藏路径中的哈希?我正在使用 BrowserRouter,但遇到了这个 404 问题.. 我正在使用浏览器路由器,刷新时导致 404 错误,但后来我用哈希路由器替换了浏览器路由器,但它不能正常工作。谢谢【参考方案11】:

如果您在 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>

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

【讨论】:

【参考方案12】:

我遇到了同样的问题,this 解决方案对我们有用..

背景:

我们在同一台服务器上托管多个应用程序。当我们刷新服务器时,服务器将不知道在 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!');
);

【讨论】:

【参考方案13】:

如果您使用的是 Create React App:

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

/*  /index.html  200

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

【讨论】:

如果您使用Apache HTTP Server,那么.htaccess 不起作用,请检查apache 版本,因为在version 2.3.9 and later 中默认的AllowOverrideNone 尝试将 AllowOverride 更改为 All。 check this answer【参考方案14】:

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

1) 用于漂亮 url 的用户 BrowserRouter

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

【讨论】:

获取未定义的路径 @Goutham 在 server.js 文件的顶部添加 import path from 'pathconst path = require('path') 第 2 步(使用 /* 了解所有内容)刚刚拯救了我的一天。谢谢! :) 这很有效,对于使用 redux 和关心连接路由器的人来说,请参见这个例子:github.com/supasate/connected-react-router/tree/master/examples/…【参考方案15】:

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

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

如果您正在运行 Appache,则对应于将以下内容添加到 .htaccess

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

这似乎也是Facebook自己提出的解决方案,可以找到here

【讨论】:

哇,这么简单的nginx解决方案;作为一个新的 nginx 用户,它就像一个魅力。只需复制粘贴即可。 +1 用于链接到官方 fb 文档。好样的。 作为记录:我正在使用带有 nginx 和 Angular 应用程序的 AWS 实例。这行得通。 你救了我。我试图从昨天开始解决我的问题,我厌倦了几个解决方案,但我失败了。最后你帮我解决了这个路由器问题。啊,非常感谢兄弟 "react": "^16.8.6", "react-router": "^5.0.1" 有什么不同吗?我尝试了这些解决方案(nginx 和 apache),但它们不起作用。 由于 Facebook 没有更改其关于此的文档,我只能假设程序是相同的。不过我有一段时间没试过了。【参考方案16】:

将此添加到webpack.config.js

devServer: 
    historyApiFallback: true

【讨论】:

这对开发人员来说很棒,但对生产构建没有帮助。 谢谢你... 真的把它修好了 为我工作! ?【参考方案17】:

使用 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

【讨论】:

【参考方案18】:

如果您在 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 错误,并且不需要使用哈希历史记录。

【讨论】:

这简单解决了我们的问题,谢谢【参考方案19】:

如果有人在这里使用 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' 时,它首先加载到服务器端。由于在服务器端,此位置没有任何内容,因此返回未找到。当你有那个可选的路由器时,它会加载同一个页面并且反应路由器也被加载,然后反应加载器决定显示哪个组件。 希望这会有所帮助。

【讨论】:

是否可以将一个确切的 URI 加载到服务器然后服务器重定向到 React?例如,当我加载/user/userID 时,我只需要返回一个通用的/user HTML 视图,带上 ID 并使用 AJAX 或 axios 调用它。有可能这样做吗?他们对 SEO 友好吗? 我将此代码用于我的带有 Laravel 后端的 SPA:Route::any('any?', 'MyController@index'); 它匹配任何路由并将其传递给 SPA【参考方案20】:

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

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

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

Error Pages 403 Rule

【讨论】:

这正是我所需要的。谢谢。 所以.. 你所有的网址都可以工作,但返回状态码 403?这是不正确的。预期的状态代码应在 2xx 范围内。 @StijndeWitt 我不确定你的理解是否正确。 CloudFront 将处理一切 - 客户端不会看到任何 403 状态代码。重新路由发生在服务器端,呈现索引页面,维护 url,并提供正确的路由作为结果。 感谢@th3morg。此设置是否也适用于多级路径?如果根级别的任何路径(例如域/注册、域/登录、域/)对我来说非常有用,但它不适用于多级路径,例如域/文档/ 哦,你这个美丽的存在!需要将其设置为 404 响应,它就像一个魅力!【参考方案21】:

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

【讨论】:

【参考方案22】:

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

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

【讨论】:

这是最简单的解决方案。请注意,这条路线应该任何其他路线之后走,因为它是一个全面的路线 从“”更改为“/”为我解决了类似的问题。但是,浏览器在刷新或 URL 导航时收到了一个空的 HTML,而不是作为 OP 收到错误“无法 GET”。【参考方案23】:

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

<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是重要的部分

【讨论】:

这很棒!是否有任何链接可以详细了解您如何获得/找到该解决方案? 对不起,兄弟。通过反复试验的可靠技术找到它。 值得一提的是,如果您使用不是/ 的基本href,那么HashRouter 将不起作用(如果您使用的是哈希路由) 标签为我做到了。谢谢你。 Webpack 开发服务器不断尝试从路径/ 中获取包,但失败了。 我正在使用 React.js + Webpack 模式。我在 package.json 文件中添加了 --history-api-fallback 参数。然后页面刷新工作正常。每次更改代码时,网页都会自动刷新。 ``` "scripts": "start": "rimraf build && cross-env NODE_ENV='development' webpack --mode development && cross-env NODE_ENV=development webpack-dev-server --history-api-fallback", ... ```【参考方案24】:

对于那些使用 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 页面,因为没有可用页面。执行以下操作:

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

它现在可以正常工作了。

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

【讨论】:

搜索 3-4 小时后的最佳解决方案。非常感谢:)【参考方案25】:

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

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

RewriteRule ^ /index.html [L]  

【讨论】:

谢谢,这对我在 Fedora 28 和 apache 上有用。上述重写规则和 CRA 页面上的规则都不适合我。我将它们添加到虚拟主机设置中,而不是在单独的 .htaccess 文件中。【参考方案26】:

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

例如:

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

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

编码愉快...

进一步阅读该主题:

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

Firebase CLI: "Configure as a single-page app (rewrite all urls to /index.html)"

【讨论】:

你是个传奇 非常感谢。很棒的解决方案!【参考方案27】:

我喜欢这种处理方式。尝试添加: yourSPAPageRoute/* 在服务器端摆脱这个问题。

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

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

Express Route

经过测试,只是想分享一下。

希望对你有帮助。

【讨论】:

【参考方案28】:

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

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

module.exports = 
  entry: './app/index.js',
  output: 
       path: path.join(__dirname, '/bundle'),
       filename: 'index_bundle.js',
       publicPath: '/'
  ,

【讨论】:

【参考方案29】:

我为我的 SPA 找到了带有反应路由器 (Apache) 的解决方案。只需添加 .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

【讨论】:

【参考方案30】:

带有示例的完整路由器(React Router v4):

示例工作示例查看下面的项目。

react-router-4-example

下载后 1.) cmd中的“npm install”安装项目 2.) "npm start" 启动你的 react 应用

注意:您需要 Node js 软件才能在您的 Windows 中运行。 Mac OS 有节点默认值。

干杯

【讨论】:

以上是关于手动刷新或写入时,React-router url 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

刷新浏览器时如何使react-router v4:id-path工作?

react-router 如何在页面刷新时保持位置状态?

react-router browserHistory刷新页面404问题解决方法

React-router typed-url 并在 tomcat、弹性 beanstalk 上刷新

react-router 同一路由,参数不同,页面没有刷新

在 react-router 中路由嵌套 url