协慌网

登录 贡献 社区

手动刷新或编写时,React-router 网址不起作用

我正在使用 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不需要向任何服务器询问任何内容。

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

答案

查看有关已接受答案的评论以及该问题的一般性质(“不起作用”),我认为这可能是对此处涉及的问题进行一些一般性解释的好地方。因此,此答案旨在作为 OP 的特定用例的背景信息 / 阐述。请多多包涵。

服务器端与客户端

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

使用客户端路由(这是 React-Router 提供的功能),事情就变得不那么简单了。首先,客户端尚未加载任何 JS 代码。因此,第一个请求将始终是服务器。然后将返回一个页面,其中包含用于加载 React 和 React Router 等所需的脚本标记。仅在加载了这些脚本后,阶段 2 才会启动。在第 2 阶段中,例如,当用户单击 “关于我们” 导航链接时,URLhttp://example.com/about (可通过History API进行更改),而无需向服务器已制成。相反,React Router 在客户端执行其操作,确定要渲染的 React 视图并进行渲染。假设您的 About 页面不需要进行任何 REST 调用,那么它已经完成了。您已经从 “首页” 过渡到 “关于我们”,而没有触发任何服务器请求。

因此,基本上,当您单击链接时,会运行一些 Javascript 来操纵地址栏中的 URL,而不会导致页面刷新,这又导致 React Router在客户端执行页面转换。

但是现在考虑如果将 URL 复制粘贴到地址栏中并将其通过电子邮件发送给朋友,会发生什么情况。您的朋友尚未加载您的网站。换句话说,她仍处于阶段 1 。她的机器上还没有运行 React Router。因此,她的浏览器将向http://example.com/about发出服务器请求

这就是您的麻烦开始的地方。到现在为止,您只需将静态 HTML 放置在服务器的 webroot 上就可以摆脱困境。但是,从服务器请求时,所有其他 URL 都404错误。这些相同的 URL在客户端可以正常工作,因为那里的 React Router 正在为您进行路由,但是除非您使服务器理解它们,否则它们在服务器端将失败。

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

如果希望http://example.com/about URL 在服务器端和客户端上都可以使用,则需要在服务器端和客户端上为其设置路由。有道理吧?

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

完全绕开该问题:哈希历史记录

使用哈希历史记录而不是浏览器历史记录,“关于” 页面的 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 本身发布的内容:

或从社区中选择众多方法之一。现在有一个不错的站点尝试索引所有这些站点:

我从这些开始:

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

祝您一切顺利!

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

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

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

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

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