Javascript 手动刷新或写入时,React-router url 不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27928372/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
React-router urls don't work when refreshing or writing manually
提问by DavidDev
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/joblistand everything is fine because I arrived here pressing a link. But If I refresh the webpage I get:
例如,我在localhost/joblist,一切都很好,因为我是按链接到达这里的。但是如果我刷新网页,我会得到:
Cannot GET /joblist
By default, it didn't work like this. Initially I had my URL as localhost/#/and localhost/#/joblistand they worked perfectly fine. But I don't like this kind of URL, so trying to erase that #, I wrote:
默认情况下,它不是这样工作的。起初,我有我的网址localhost/#/,并localhost/#/joblist和他们工作完全正常。但我不喜欢这种 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 /joblistdoesn'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);
});
回答by Stijn de Witt
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. Please bear with me.
查看对已接受答案的评论以及此问题的一般性质(“不起作用”),我认为这可能是对此处涉及的问题进行一些一般性解释的好地方。因此,此答案旨在作为 OP 特定用例的背景信息/详细说明。请多多包涵。
Server-side vs Client-side
服务器端 vs 客户端
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'. In the past, when life was simple, some user sent a request for http://example.com/aboutto the server, which inspected the path part of the URL, determined the user was requesting the about page and then sent back that page.
要了解的第一件大事是现在有 2 个地方可以解释 URL,而过去在“过去”只有 1 个地方。过去,生活简单时,有些用户向http://example.com/about服务器发送请求,服务器检查 URL 的路径部分,确定用户请求的是关于页面,然后返回该页面。
With client-side routing, which is what React-Router provides, things are less simple. At first, the client does not have any JS code loaded yet. 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. In phase 2, when the user clicks on the 'About us' navigation link for example, the URL is changed locally onlyto http://example.com/about(made possible by the History API), but no request to the server is made. Instead, React Router does its thing on the client side, determines which React view to render and renders it. Assuming your about page does not need to make any REST calls, it's done already. You have transitioned from Home to About Us without any server request having fired.
使用 React-Router 提供的客户端路由,事情就不那么简单了。首先,客户端还没有加载任何 JS 代码。因此,第一个请求将始终发送到服务器。然后将返回一个页面,其中包含加载 React 和 React Router 等所需的脚本标签。只有当这些脚本加载后,阶段 2 才会开始。在第 2 阶段,例如,当用户单击“关于我们”导航链接时,URL仅在本地更改为http://example.com/about(通过History API 实现),但不会向服务器发出请求. 相反,React Router 在客户端做它的事情,确定要呈现和呈现它的 React 视图。假设您的关于页面不需要进行任何 REST 调用,它已经完成了。您已从主页转换到关于我们,而没有触发任何服务器请求。
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. Your friend has not loaded your website yet. In other words, she is still in phase 1. No React Router is running on her machine yet. So her browser will make a server requestto http://example.com/about.
但是现在考虑如果您将 URL 复制粘贴到地址栏中并将其通过电子邮件发送给朋友会发生什么。您的朋友尚未加载您的网站。也就是说,她还处于第一阶段。她的机器上还没有运行 React Router。所以,她的浏览器将使服务器的请求来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. But that would give 404errors for all other URLs when requested from the server. 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 sideunless you make your server understand them.
这就是你的麻烦开始的地方。到目前为止,您只需在服务器的 webroot 中放置一个静态 HTML 即可。但是,当从服务器请求时404,所有其他 URL都会出错。那些相同的 URL在客户端工作正常,因为 React Router 正在为你做路由,但它们在服务器端失败,除非你让你的服务器理解它们。
Combining server- and client-side routing
结合服务器端和客户端路由
If you want the http://example.com/aboutURL 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. Makes sense right?
如果您希望http://example.com/aboutURL 在服务器端和客户端都工作,则需要在服务器端和客户端为其设置路由。有道理吗?
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 Historyinstead of Browser History, your URL for the about page would look something like this:
http://example.com/#/aboutThe part after the hash (#) symbol is not sent to the server. So the server only sees http://example.com/and sends the index page as expected. React-Router will pick up the #/aboutpart and show the correct page.
使用Hash History而不是Browser History,关于页面的 URL 将如下所示:
http://example.com/#/about哈希 ( #) 符号后面的部分不会发送到服务器。因此,服务器仅http://example.com/按预期查看和发送索引页面。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. You do have clean URLs however and you could improve upon this scheme later without having to invalidate all your user's favorites.
通过这种方法,您确实使用了浏览器历史记录,但只需在发送/*到的服务器上设置一个包罗万象的功能index.html,有效地为您提供与哈希历史记录大致相同的情况。但是,您确实有干净的 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
- Duplicating code for rendering content on server and client
- 设置更复杂
- 对于您给予特殊待遇的那些路线,只有良好的 SEO
- 复制用于在服务器和客户端上呈现内容的代码
.
.
Isomorphic
同构
What if we use Node JS as our server so we can run the sameJS code on both ends? Now, we have all our routes defined in a single react-router config and we don't need to duplicate our rendering code. 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.
如果我们使用 Node JS 作为我们的服务器,这样我们就可以在两端运行相同的JS 代码呢?现在,我们在单个 react-router 配置中定义了所有路由,我们不需要复制渲染代码。可以这么说,这就是“圣杯”。如果页面转换发生在客户端,服务器会发送与我们最终得到的完全相同的标记。该解决方案在 SEO 方面是最佳的。
Downsides:
缺点:
- Server must(be able to) run JS. I've experimented with Java i.c.w. Nashorn but it's not working for me. In practice it mostly means you must use a Node JS based server.
- Many tricky environmental issues (using
windowon server-side etc) - Steep learning curve
- 服务器必须(能够)运行 JS。我已经尝试过 Java icw Nashorn,但它对我不起作用。实际上,这主要意味着您必须使用基于 Node JS 的服务器。
- 许多棘手的环境问题(
window在服务器端使用等) - 陡峭的学习曲线
.
.
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. Yes it's tough at first, but once you get the hang of it it's actually a very elegant solution to the problem.
选择一个你可以逃脱的。就我个人而言,我认为全能设置足够简单,所以这将是我的最低要求。此设置可让您随着时间的推移不断改进。如果你已经在使用 Node JS 作为你的服务器平台,我肯定会研究做一个同构应用程序。是的,一开始很难,但是一旦掌握了窍门,它实际上是解决问题的非常优雅的方法。
So basically, for me, that would be the deciding factor. If my server runs on Node JS, I'd go isomorphic; otherwise I would go for the Catch-all solution and just expand on it (Hybrid solution) as time progresses and SEO requirements demand it.
所以基本上,对我来说,这将是决定性因素。如果我的服务器在 Node JS 上运行,我会去同构;否则,我会选择 Catch-all 解决方案,并随着时间的推移和 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 的同构(也称为“通用”)渲染,有一些关于这个主题的很好的教程:
- 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
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). Start with looking at the one published by Facebook itself:
此外,为了让您入门,我建议您查看一些入门套件。选择与您的技术堆栈选择相匹配的一个(请记住,React 只是 MVC 中的 V,您需要更多的东西来构建完整的应用程序)。首先看看 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!
祝你的任务好运!
回答by jmancherje
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. Now routing works correctly and I can refresh the page or type in the URL directly. No need to worry about work arounds on your node server. This answer obviously only works if you're using webpack.
historyApiFallback 为我解决了这个问题。现在路由正常工作,我可以刷新页面或直接输入 URL。无需担心节点服务器上的变通方法。这个答案显然只在你使用 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
回答by BrahimS
You can change your .htaccessfile and insert this:
您可以更改.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>
I am using react: "^16.12.0"and react-router: "^5.1.2"This method is the Catch-all and is probably the easiest way to get you started.
我正在使用react: "^16.12.0"和react-router: "^5.1.2"这种方法是万能的,可能是让您入门的最简单方法。
回答by user2875289
For React Router V4Users:
对于React Router V4用户:
If you try to solve this problem by Hash History technique mentioned in other answers, note that
如果您尝试通过其他答案中提到的哈希历史技术解决此问题,请注意
<Router history={hashHistory} >
does not work in V4, please use HashRouterinstead:
在 V4 中不起作用,请HashRouter改用:
import { HashRouter } from 'react-router-dom'
<HashRouter>
<App/>
</HashRouter>
Reference: HashRouter
参考:HashRouter
回答by Todd
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.
可以通过两种不同的方式调用路由器,具体取决于导航发生在客户端还是服务器上。您已将其配置为客户端操作。关键参数是run 方法的第二个参数,位置。
When you use the React Router Link component, it blocks browser navigation and calls transitionTo to do a client-side navigation. 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. If you're using older browsers, this won't work. You would need to use the HashLocation component.
当您使用 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 /joblistand it must return something. On the server you need to pass the path that was requested to the runmethod 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.
当您点击刷新时,您将绕过所有 React 和 React Router 代码。服务器收到请求/joblist,它必须返回一些东西。在服务器上,您需要将请求的路径传递给该run方法,以便它呈现正确的视图。您可以使用相同的路线图,但您可能需要对Router.run. 正如 Charles 指出的那样,您可以使用 URL 重写来处理此问题。另一种选择是使用 node.js 服务器来处理所有请求并将路径值作为位置参数传递。
In express, for example, it might look like this:
例如,在 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 });
});
});
Note that the request path is being passed to run. To do this, you'll need to have a server-side view engine that you can pass the rendered HTML to. There are a number of other considerations using renderToStringand in running React on the server. 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.
请注意,请求路径正在传递给run. 为此,您需要有一个服务器端视图引擎,您可以将呈现的 HTML 传递给它。renderToString在服务器上使用和运行 React 时还有许多其他注意事项。一旦页面在服务器上呈现,当您的应用程序在客户端加载时,它将再次呈现,根据需要更新服务器端呈现的 HTML。
回答by Efe Ariaroo
In your index.html head, add the following:
在您的 index.html 中head,添加以下内容:
<base href="/">
<!-- This must come before the css and javascripts -->
Then when running with webpack dev server use this command.
然后在使用 webpack dev server 运行时使用此命令。
webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback
--history-api-fallbackis the important part
--history-api-fallback是重要的部分
回答by Aidin
I used create-react-app to make a website just now and had the same issue presented here. I use BrowserRoutingfrom the react-router-dompackage. I am running on a Nginx server and what solved it for me was adding the following to /etc/nginx/yourconfig.conf
我刚刚使用 create-react-app 制作了一个网站,并在此处提出了相同的问题。我BrowserRouting从react-router-dom包中使用。我在 Nginx 服务器上运行,为我解决的问题是将以下内容添加到/etc/nginx/yourconfig.conf
location / {
if (!-e $request_filename){
rewrite ^(.*)$ /index.html break;
}
}
Which corresponds to adding the following to the .htaccessin case you are running Appache
.htaccess如果您正在运行 Appache,这对应于将以下内容添加到
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
This also seems to be the solution suggested by Facebook themselves and can be found here
这似乎也是 Facebook 自己建议的解决方案,可以在这里找到
回答by Venkatesh Somu
This can solve your problem
这可以解决您的问题
I also faced the same problem in ReactJS application in Production mode. Here is the 2 solution to the problem.
我在生产模式下的 ReactJS 应用程序中也遇到了同样的问题。这是问题的2解决方案。
1.Change the routing history to "hashHistory" instead of browserHistory in the place of
1.将路由历史改为“hashHistory”而不是browserHistory
<Router history={hashHistory} >
<Route path="/home" component={Home} />
<Route path="/aboutus" component={AboutUs} />
</Router>
Now build the app using the command
现在使用命令构建应用程序
sudo npm run build
Then place the build folder in your var/www/ folder, Now the application is working fine with addition of # tag in each and every url. like
然后将 build 文件夹放在 var/www/ 文件夹中,现在应用程序工作正常,在每个 url 中添加 # 标签。喜欢
localhost/#/home localhost/#/aboutus
本地主机/#/home 本地主机/#/aboutus
Solution 2 : Without # tag using browserHistory,
解决方案 2 :没有 # 标签使用 browserHistory,
Set your history = {browserHistory} in your Router,Now build it using sudo npm run build.
在您的路由器中设置您的历史记录 = {browserHistory},现在使用 sudo npm run build 构建它。
You need to create the "conf" file to solve the 404 not found page, the conf file should be like this.
您需要创建“conf”文件来解决404 not found页面,conf文件应该是这样的。
open your terminal type the below commands
打开你的终端输入以下命令
cd /etc/apache2/sites-available ls nano sample.conf Add the below content in it.
cd /etc/apache2/sites-available ls nano sample.conf 加入以下内容。
<VirtualHost *:80>
ServerAdmin [email protected]
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>
Now you need to enable the sample.conf file by using the following command
现在您需要使用以下命令启用 sample.conf 文件
cd /etc/apache2/sites-available
sudo a2ensite sample.conf
then it will ask you to reload the apache server,using sudo service apache2 reload or restart
然后它会要求你重新加载 apache 服务器,使用 sudo service apache2 reload 或 restart
then open your localhost/build folder and add the .htaccess file with content of below.
然后打开您的 localhost/build 文件夹并添加具有以下内容的 .htaccess 文件。
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^.*$ / [L,QSA]
Now the app is working normally.
现在该应用程序正常运行。
Note: change 0.0.0.0 ip to your local ip address.
注意:将 0.0.0.0 ip 更改为您的本地 IP 地址。
If any doubts regarding this feel free to raise a comment.
如果对此有任何疑问,请随时提出评论。
I hope it is helpful to others.
我希望它对其他人有帮助。
回答by th3morg
If you're hosting a react app via AWS Static S3 Hosting & CloudFront
如果您通过 AWS 静态 S3 托管和 CloudFront 托管 React 应用程序
This problem presented itself by CloudFront responding with a 403 Access Denied message because it expected /some/other/path to exist in my S3 folder, but that path only exists internally in React's routing with react-router.
这个问题是由 CloudFront 以 403 Access Denied 消息响应而出现的,因为它期望 /some/other/path 存在于我的 S3 文件夹中,但该路径仅存在于 React 的内部路由与 react-router 中。
The solution was to set up a distribution Error Pages rule. Go to the CloudFront settings and choose your distribution. Next go to the "Error Pages" tab. Click "Create Custom Error Response" and add an entry for 403 since that's the error status code we get. Set the Response Page Path to /index.html and the status code to 200. The end result astonishes me with its simplicity. The index page is served, but the URL is preserved in the browser, so once the react app loads, it detects the URL path and navigates to the desired route.
解决方案是设置分发错误页面规则。转到 CloudFront 设置并选择您的分配。接下来转到“错误页面”选项卡。单击“创建自定义错误响应”并为 403 添加一个条目,因为这是我们得到的错误状态代码。将响应页面路径设置为 /index.html 并将状态代码设置为 200。最终结果的简单性让我感到惊讶。提供索引页面,但 URL 保留在浏览器中,因此一旦 react 应用程序加载,它就会检测 URL 路径并导航到所需的路由。
回答by srijishks
The Webpack Dev Server has an option to enable this. Open up package.jsonand add --history-api-fallback.
This solutions worked for me.
Webpack Dev Server 有一个选项来启用它。打开package.json并添加--history-api-fallback. 这个解决方案对我有用。

