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

贾沛
2023-12-01

本文翻译自:React-router urls don't work when refreshing or writing manually

I'm using React-router and it works fine while I'm clicking on link buttons, but when I refresh my webpage it does not load what I want. 我正在使用React-router,当我单击链接按钮时,它可以正常工作,但是当我刷新网页时,它不会加载我想要的内容。

For instance, I am in localhost/joblist and everything is fine because I arrived here pressing a link. 例如,我在localhost/joblist ,一切都很好,因为我按链接到达此处。 But If I refresh the webpage I get: 但是,如果刷新网页,则会得到:

Cannot GET /joblist

By default, it didn't work like this. 默认情况下,它不是这样工作的。 Initially I had my URL as localhost/#/ and localhost/#/joblist and they worked perfectly fine. 最初,我的URL为localhost/#/localhost/#/joblist ,它们工作得很好。 But I don't like this kind of URL, so trying to erase that # , I wrote: 但是我不喜欢这种URL,因此尝试删除# ,我写道:

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

This problem does not happen with localhost/ , this one always returns what I want. localhost/不会发生此问题,这总是返回我想要的。

EDIT: This app is single-page, so /joblist doesn't need to ask anything to any server. 编辑:此应用程序是单页,因此/joblist不需要向任何服务器询问任何内容。

EDIT2: My entire router. 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);
});

#1楼

参考:https://stackoom.com/question/1tBRc/手动刷新或写入时-React-router网址不起作用


#2楼

The router can be called in two different ways, depending on whether the navigation occurs on the client or on the server. 可以通过两种不同的方式调用路由器,具体取决于导航是在客户端还是在服务器上进行。 You have it configured for client-side operation. 您已将其配置为用于客户端操作。 The key parameter is the second one to the run method , the location. 关键参数是运行方法的第二个参数,即位置。

When you use the React Router Link component, it blocks browser navigation and calls transitionTo to do a client-side navigation. 当您使用React Router Link组件时,它会阻止浏览器导航并调用transitionTo进行客户端导航。 You are using HistoryLocation, so it uses the HTML5 history API to complete the illusion of navigation by simulating the new URL in the address bar. 您使用的是HistoryLocation,因此它使用HTML5历史记录API通过模拟地址栏中的新URL来完成导航的效果。 If you're using older browsers, this won't work. 如果您使用的是较旧的浏览器,则无法使用。 You would need to use the HashLocation component. 您将需要使用HashLocation组件。

When you hit refresh, you bypass all of the React and React Router code. 当您点击刷新时,您将绕过所有React和React Router代码。 The server gets the request for /joblist and it must return something. 服务器获取对/joblist的请求,并且它必须返回某些内容。 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. 在服务器上,您需要将请求的路径传递给run方法,以使其呈现正确的视图。 You can use the same route map, but you'll probably need a different call to Router.run . 您可以使用相同的路线图,但可能需要对Router.run进行其他调用。 As Charles points out, you can use URL rewriting to handle this. 正如Charles所指出的,您可以使用URL重写来处理此问题。 Another option is to use a node.js server to handle all requests and pass the path value as the location argument. 另一个选择是使用node.js服务器来处理所有请求,并将路径值作为location参数传递。

In express, for example, it might look like this: 例如,在快递中,它可能看起来像这样:

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

Note that the request path is being passed to run . 请注意,请求路径正在传递给run To do this, you'll need to have a server-side view engine that you can pass the rendered HTML to. 为此,您需要有一个服务器端视图引擎,您可以将呈现的HTML传递到该引擎。 There are a number of other considerations using renderToString and in running React on the server. 使用renderToString以及在服务器上运行React时,还有许多其他注意事项。 Once the page is rendered on the server, when your app loads in the client, it will render again, updating the server-side rendered HTML as needed. 页面在服务器上呈现后,当您的应用加载到客户端时,它将再次呈现,并根据需要更新服务器端呈现的HTML。


#3楼

Looking at the comments on the accepted answer and the generic nature of this question ('don't work'), I thought this might be a good place for some general explanations about the issues involved here. 查看有关已接受答案的评论以及该问题的一般性质(“不起作用”),我认为这可能是对此处涉及的问题进行一些一般性解释的好地方。 So this answer is intended as background info / elaboration on the specific use case of the OP. 因此,此答案旨在作为OP的特定用例的背景信息/阐述。 Please bear with me. 请多多包涵。

Server-side vs Client-side 服务器端与客户端

The first big thing to understand about this is that there are now 2 places where the URL is interpreted, whereas there used to be only 1 in 'the old days'. 首先要了解的是,现在有2个地方可以解释URL,而在“过去”中,以前只有1个地方。 In the past, when life was simple, some user sent a request for http://example.com/about to the server, which inspected the path part of the URL, determined the user was requesting the about page and then sent back that page. 过去,当生活很简单时,一些用户向服务器发送了对http://example.com/about的请求,该服务器检查URL的路径部分,确定该用户正在请求about页面,然后将其发送回页。

With client-side routing, which is what React-Router provides, things are less simple. 使用客户端路由(这是React-Router提供的功能),事情就变得不那么简单了。 At first, the client does not have any JS code loaded yet. 首先,客户端尚未加载任何JS代码。 So the very first request will always be to the server. 因此,第一个请求将始终是服务器。 That will then return a page that contains the needed script tags to load React and React Router etc. Only when those scripts have loaded does phase 2 start. 然后,这将返回一个页面,其中包含用于加载React和React Router等所需的脚本标签。仅在加载了这些脚本后,阶段2才会启动。 In phase 2, when the user clicks on the 'About us' navigation link for example, the URL is changed locally only to http://example.com/about (made possible by the History API ), but no request to the server is made . 在第2阶段中,例如,当用户单击“关于我们”导航链接时,URL 本地更改为http://example.com/about (由History API设置 ),但没有对服务器的请求被制造 Instead, React Router does its thing on the client side, determines which React view to render and renders it. 相反,React Router在客户端执行其操作,确定要渲染的React视图并进行渲染。 Assuming your about page does not need to make any REST calls, it's done already. 假设您的About页面不需要进行任何REST调用,那么它已经完成了。 You have transitioned from Home to About Us without any server request having fired. 您已从首页转换为关于我们,而没有触发任何服务器请求。

So basically when you click a link, some Javascript runs that manipulates the URL in the address bar, without causing a page refresh , which in turn causes React Router to perform a page transition on the client side . 因此,基本上,当您单击链接时,会运行一些Javascript来操纵地址栏中的URL, 而不会导致页面刷新 ,这又导致React Router 在客户端执行页面转换。

But now consider what happens if you copy-paste the URL in the address bar and e-mail it to a friend. 但是,现在考虑如果将URL复制粘贴到地址栏中并将其通过电子邮件发送给朋友,会发生什么情况。 Your friend has not loaded your website yet. 您的朋友尚未加载您的网站。 In other words, she is still in phase 1 . 换句话说,她仍处于阶段1 No React Router is running on her machine yet. 她的机器上还没有运行React Router。 So her browser will make a server request to http://example.com/about . 因此,她的浏览器将向http://example.com/about发出服务器请求

And this is where your trouble starts. 这就是您麻烦的开始。 Until now, you could get away with just placing a static HTML at the webroot of your server. 到现在为止,您只需将静态HTML放置在服务器的webroot上就可以摆脱困境。 But that would give 404 errors for all other URLs when requested from the server . 但是, 从服务器请求时 ,所有其他URL都会产生404错误。 Those same URLs work fine on the client side , because there React Router is doing the routing for you, but they fail on the server side unless you make your server understand them. 这些相同的URL 在客户端可以正常工作,因为那里的React Router正在为您进行路由,但是除非您使服务器理解它们,否则它们在服务器端将失败。

Combining server- and client-side routing 结合服务器端和客户端路由

If you want the http://example.com/about URL to work on both the server- and the client-side, you need to set up routes for it on both the server- and the client side. 如果您希望http://example.com/about URL在服务器端和客户端上都能使用,则需要在服务器端和客户端上为其设置路由。 Makes sense right? 有道理吧?

And this is where your choices begin. 这就是您开始选择的地方。 Solutions range from bypassing the problem altogether, via a catch-all route that returns the bootstrap HTML, to the full-on isomorphic approach where both the server and the client run the same JS code. 解决方案的范围从完全绕过问题(通过返回引导HTML的包罗万象的路线)到完全同构的方法(在该方法中,服务器和客户端都运行相同的JS代码)。

.

Bypassing the problem altogether: Hash History 完全绕开该问题:哈希历史记录

With Hash History instead of Browser History , your URL for the about page would look something like this: http://example.com/#/about The part after the hash ( # ) symbol is not sent to the server. 使用哈希历史记录而不是浏览器历史记录 ,“关于”页面的URL如下所示: http://example.com/#/about : http://example.com/#/about哈希号( # )符号后的部分未发送到服务器。 So the server only sees http://example.com/ and sends the index page as expected. 因此,服务器仅看到http://example.com/并按预期发送索引页面。 React-Router will pick up the #/about part and show the correct page. React-Router将拾取#/about部分并显示正确的页面。

Downsides : 缺点

  • 'ugly' URLs “丑陋”的网址
  • Server-side rendering is not possible with this approach. 使用这种方法无法进行服务器端渲染。 As far as Search Engine Optimization (SEO) is concerned, your website consists of a single page with hardly any content on it. 就搜索引擎优化(SEO)而言,您的网站只有一个页面,几乎没有任何内容。

.

Catch-all 包罗万象

With this approach you do use Browser History, but just set up a catch-all on the server that sends /* to index.html , effectively giving you much the same situation as with Hash History. 通过这种方法,您确实使用了浏览器历史记录,但是只是在将/*发送到index.html的服务器上设置了一个index.html ,有效地为您提供了与哈希历史记录相同的情况。 You do have clean URLs however and you could improve upon this scheme later without having to invalidate all your user's favorites. 但是,您确实拥有干净的URL,以后可以改进此方案而不必使所有用户的收藏夹无效。

Downsides : 缺点

  • More complex to set up 设置更复杂
  • Still no good SEO 仍然没有好的SEO

.

Hybrid 杂种

In the hybrid approach you expand upon the catch-all scenario by adding specific scripts for specific routes. 在混合方法中,您可以通过为特定路由添加特定脚本来扩展包罗万象的方案。 You could make some simple PHP scripts to return the most important pages of your site with content included, so Googlebot can at least see what's on your page. 您可以编写一些简单的PHP脚本来返回网站上最重要的页面,其中包含内容,因此Googlebot至少可以看到页面上的内容。

Downsides : 缺点

  • Even more complex to set up 设置起来更加复杂
  • Only good SEO for those routes you give the special treatment 对于那些给予特殊待遇的路线,只有好的SEO
  • Duplicating code for rendering content on server and client 复制代码以在服务器和客户端上呈现内容

.

Isomorphic 同构

What if we use Node JS as our server so we can run the same JS code on both ends? 如果我们使用Node JS作为服务器,以便我们可以在两端运行相同的 JS代码怎么办? Now, we have all our routes defined in a single react-router config and we don't need to duplicate our rendering code. 现在,我们所有的路由都在一个react-router配置中定义,我们不需要复制渲染代码。 This is 'the holy grail' so to speak. 可以这么说,这是“圣杯”。 The server sends the exact same markup as we would end up with if the page transition had happened on the client. 如果客户端发生页面转换,服务器将发送与最终相同的标记。 This solution is optimal in terms of SEO. 就SEO而言,此解决方案是最佳的。

Downsides : 缺点

  • Server must (be able to) run JS. 服务器必须 (能够)运行JS。 I've experimented with Java icw Nashorn but it's not working for me. 我已经尝试过Java icw Nashorn,但是它对我不起作用。 In practice it mostly means you must use a Node JS based server. 实际上,这主要意味着您必须使用基于Node JS的服务器。
  • Many tricky environmental issues (using window on server-side etc) 许多棘手的环境问题(在服务器端使用window等)
  • Steep learning curve 陡峭的学习曲线

.

Which should I use? 我应该使用哪个?

Choose the one that you can get away with. 选择一个您可以摆脱的。 Personally I think the catch-all is simple enough to set up, so that would be my minimum. 我个人认为,包罗万象的设置程序非常简单,所以这是我的最低要求。 This setup allows you to improve on things over time. 通过此设置,您可以随着时间的流逝而改进。 If you are already using Node JS as your server platform, I'd definitely investigate doing an isomorphic app. 如果您已经在使用Node JS作为服务器平台,那么我肯定会研究使用同构应用程序。 Yes it's tough at first, but once you get the hang of it it's actually a very elegant solution to the problem. 是的,一开始很难,但是一旦掌握了它,它实际上是解决问题的一种非常优雅的方法。

So basically, for me, that would be the deciding factor. 因此,对我而言,基本上,这就是决定性因素。 If my server runs on Node JS, I'd go isomorphic; 如果我的服务器在Node JS上运行,我将同构。 otherwise I would go for the Catch-all solution and just expand on it (Hybrid solution) as time progresses and SEO requirements demand it. 否则,我会选择全包解决方案,并随着时间的推移和SEO要求对其进行扩展(混合解决方案)。

If you'd like to learn more on isomorphic (also called 'universal') rendering with React, there are some good tutorials on the subject: 如果您想了解更多有关使用React进行同构(也称为“通用”)渲染的知识,那么有一些关于该主题的不错的教程:

Also, to get you started, I recommend looking at some starter kits. 另外,为使您入门,我建议您看一些入门套件。 Pick one that matches your choices for the technology stack (remember, React is just the V in MVC, you need more stuff to build a full app). 选择一个与您选择的技术相匹配的技术(请记住,React只是MVC中的V,您需要更多的东西来构建完整的应用程序)。 Start with looking at the one published by Facebook itself: 首先看一下Facebook本身发布的内容:

Or pick one of the many by the community. 或从社区中选择许多。 There is a nice site now that tries to index all of them: 现在有一个不错的站点尝试索引所有这些站点:

I started with these: 我从这些开始:

Currently I am using a home-brew version of universal rendering that was inspired by the two starter kits above, but they are out of date now. 目前,我使用的是通用的自制版本,该版本受上述两个入门工具包的启发,但现在已经过时了。

Good luck with your quest! 祝您一切顺利!


#4楼

If you do have a fallback to your index.html, make sure that in your index.html file you have this: 如果确实有index.html的备用版本,请确保在index.html文件中具有以下内容:

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

This may differ from project to project. 这可能因项目而异。


#5楼

The answers here are all extremely helpful, what worked for me was configuring my Webpack server to expect the routes. 这里的答案都是非常有用的,对我有用的是配置Webpack服务器以期望路由。

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

The historyApiFallback is what fixed this issue for me. historyApiFallback是为我解决此问题的原因。 Now routing works correctly and I can refresh the page or type in the URL directly. 现在,路由可以正常工作,我可以刷新页面或直接输入URL。 No need to worry about work arounds on your node server. 无需担心节点服务器上的变通方法。 This answer obviously only works if you're using webpack. 该答案显然仅在使用webpack时有效。

EDIT: see my answer here for a more detailed reason why this is necessary: https://stackoverflow.com/a/37622953/5217568 编辑:请参阅我的答案以了解为什么这是必要的更详细的原因: https : //stackoverflow.com/a/37622953/5217568


#6楼

The Webpack Dev Server has an option to enable this. Webpack Dev Server具有启用此功能的选项。 Open up package.json and add --history-api-fallback . 打开package.json并添加--history-api-fallback This solutions worked for me. 该解决方案为我工作。

react-router-tutorial 反应路由器教程

 类似资料: