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

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

例如,我进入localhost / joblist,一切都很好,因为我到达这里按下一个链接。 但是,如果我刷新网页,我得到:无法GET / joblist

默认情况下,它不这样工作。 最初我有我的URL:本地主机/#/和本地主机/#/工作清单,他们工作得很好。 但我不喜欢这种types的url,所以试图擦除'#'我写道:

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

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

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

编辑2:我的整个路由器。

 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的具体用例的背景信息/阐述。 请多多包涵。

服务器端与客户端

要理解的第一件大事就是现在有两个地方可以解释URL,而过去只有一个。 在过去,当生活很简单时,一些用户向服务器发送了http://example.com/about的请求,检查URL的path部分,确定用户正在请求关于页面,然后发回页。

使用React-Router提供的客户端路由,事情并不简单。 起初,客户端还没有加载任何JS代码。 因此,第一个请求将始终是服务器。 然后这将返回一个页面,其中包含所需的脚本标签来加载React和React路由器等只有当这些脚本已经加载的第二阶段开始。 在阶段2中,例如当用户点击“关于我们”的导航链接时,URL 本地被更改为http://example.com/about (由历史API可能),但是没有请求到服务器被制成 。 相反,React路由器在客户端做这件事情,决定渲染和呈现哪个React视图。 假设你的关于页面不需要做任何REST调用,它已经完成了。 您已经从主页转换到关于我们没有任何服务器请求已经被解雇。

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

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

这是你的麻烦开始的地方。 到现在为止,只要在服务器的webroot上放置一个静态的HTML就可以了。 但是, 当从服务器请求时 ,这将给所有其他URL提供404错误。 那些相同的URL 在客户端工作得很好,因为React Router为你做了路由,但是除非你让你的服务器理解它们,否则在服务器端会失败。

结合服务器和客户端路由

如果您希望http://example.com/about URL可以在服务器端和客户端上运行,则需要在服务器端和客户端上为其设置路由。 有意义吗?

这是你的select开始的地方。 解决scheme的范围从完全绕过这个问题,通过返回引导HTML的catch-allpath,到服务器和客户端都运行相同的JS代码的完全同构的方法。

绕过这个问题:哈希历史

使用哈希历史logging iso 浏览器历史logging ,关于页面的URL将如下所示: http://example.com/#/about : http://example.com/#/about哈希( # )符号之后的部分不会发送到服务器。 所以服务器只能看到http://example.com/并按预期发送索引页面。 React-Router会select#/about部分并显示正确的页面。

缺点

  • '丑陋'的url
  • 使用这种方法服务器端渲染是不可能的。 就search引擎优化而言,您的网站由一个几乎没有任何内容的单一页面组成。

包罗万象

使用这种方法,您可以使用浏览器历史logging,但只需在将/*发送到index.html的服务器上设置一个捕获所有方式,就可以有效地为您提供与哈希历史相同的情况。 您确实拥有干净的url,您可以稍后改进此scheme,而不必使所有用户的collections失效。

缺点

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

混合动力

在混合方法中,您可以通过为特定路由添加特定脚本来扩展全部情况。 您可以使用一些简单的PHP脚本来返回包含内容的网站最重要的页面,所以Googlebot至less可以查看您网页上的内容。

缺点

  • 更复杂的设置
  • 只有那些你给予特殊待遇的路线很好的search引擎优化
  • 复制在服务器和客户端呈现内容的代码

同构

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

缺点

  • 服务器必须 (能够)运行JS。 我已经用Java icw Nashorn进行了实验,但是它不适合我。 在实践中,它大多意味着你必须使用基于Node的JS服务器。
  • 许多棘手的环境问题(使用服务器端的window等)
  • 陡峭的学习曲线

我应该用哪个?

select一个你可以逃避。 就我个人而言,我认为总是很简单,设置这是我的最低限度。 这个设置可以让你随着时间的推移改进。 如果您已经在使用Node JS作为您的服务器平台,我肯定会调查做一个同构的应用程序。 是的,起初很困难,但是一旦掌握了这个问题,这实际上是一个非常优雅的解决scheme。

所以基本上,对我而言,这将是决定性因素。 如果我的服务器运行在Node JS上,我会去同构,否则我会去Catch-all解决scheme,随着时间的推移和search引擎优化需求的需求,扩大它(混合解决scheme)。

如果您想了解更多关于React的同构(也称为“通用”)渲染,有关于这个主题的一些很好的教程:

  • 用同构的应用程序来响应未来
  • 在ReactJS中创build同构应用程序的痛苦和快乐
  • 如何实现Node + React同构JavaScript及其重要性

另外,为了让你开始,我build议看看一些入门套件。 select一个与技术栈相匹配的选项(记住,React只是MVC中的V,你需要更多的东西来构build完整的应用程序)。 首先看看Facebook自己发布的那个:

  • 创buildReact App

或者从社区中挑选其中的一个。 现在有一个很好的网站,试图索引他们所有的人:

  • select你的完美的React启动项目

我从这些开始:

  • React同构Starterkit
  • React Redux通用热点示例

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

祝你好运!

这里的答案都非常有帮助,什么对我的工作是configuration我的Webpack服务器期望的路线。

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

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

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

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

当您使用React路由器链接组件时,它会阻止浏览器导航并调用transitionTo来执行客户端导航。 您正在使用HistoryLocation,因此它使用HTML5历史loggingAPI来模拟地址栏中的新url,从而完成导航错觉。 如果您使用的浏览器较旧,则无法使用。 您将需要使用HashLocation组件。

当你点击刷新时,你会绕过所有的React和React Router代码。 服务器获取/joblist的请求,它必须返回一些东西。 在服务器上,您需要将请求的path传递给run方法,以便呈现正确的视图。 您可以使用相同的路由映射,但您可能需要对Router.run进行不同的调用。 正如Charles指出的,你可以使用URL重写来处理这个问题。 另一种select是使用node.js服务器来处理所有请求,并将path值作为locationparameter passing。

例如,expression方式可能如下所示:

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

请注意,请求path正在传递以run 。 要做到这一点,你需要有一个服务器端的视图引擎,您可以将渲染的HTML传递给。 使用renderToString和在服务器上运行React还有很多其他的考虑因素。 一旦页面呈现在服务器上,当你的应用程序加载到客户端时,它将再次呈现,根据需要更新服务器端呈现的HTML。

你可以改变你的htaccess并插入:

 RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] i am using react: "^15.3.2" , react-router: "^3.0.0", history: "^4.3.0", 

对于React Router V4用户:

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

<Router history={hashHistory} >

在V4中不起作用,请使用HashRouter代替:

 import { HashRouter } from 'react-router-dom' <HashRouter> <App/> </HashRouter> 

参考: https : //reacttraining.com/react-router/web/api/HashRouter

Webpack Dev Server有一个选项来启用它。 打开package.json并添加--history-api-fallback 。 这个解决scheme为我工作。

https://github.com/reactjs/react-router-tutorial/tree/master/lessons/10-clean-urls#configuring-your-server

这可以解决你的问题

在生产模式下,我也遇到了ReactJS应用程序中的相同问题。 这是解决问题的两个办法。

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

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

现在使用该命令构build应用程序

 sudo npm run build 

然后将生成文件夹放在你的var / www /文件夹中,现在应用程序正常工作,并在每个url添加#标签。 喜欢

localhost /#/ home localhost /#/ aboutus

解决scheme2:如果没有使用browserHistory的#标签,

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

你需要创build“conf”文件来解决404找不到的页面,conf文件应该是这样的。

打开你的terminal键入下面的命令

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重新加载或重启

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

如有任何疑问,欢迎发表评论。

我希望这对别人有帮助。

如果你确实有一个回退你的index.html,请确保在你的index.html文件中有这样的:

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

这可能因项目而异。

我没有使用服务器端渲染,但我遇到同样的问题,其中链接似乎大部分时间罚款,但失败了,当我有一个参数。 我会在这里logging我的解决scheme,看看它是否有助于任何人。

我的主要jsx包含这个:

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

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

麻烦的是我用props.params.idcomponentDidMount设置模型。 该组件只安装一次,这意味着第一个模型是粘贴在页面上的模型,后续的链接改变了道具,但保持页面不变。

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

如果您正在使用Create React App:

对于许多主要托pipe平台的解决scheme,您可以在“创buildReact应用程序”页面上find这个问题。 例如,我使用React Router v4和Netlify作为我的前端代码。 所有这一切只是添加1个文件到我的公用文件夹(“_redirects”)和该文件中的一行代码:

 /* /index.html 200 

现在我的网站正确地呈现如mysite.com/pricing的path,当进入浏览器或有人点击刷新。

我刚刚使用了create-react-app来创build一个网站,并在这里提出了相同的问题。 我使用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自己提出的解决scheme,可以在这里find

这个话题已经有点老了,但是我想给你一个简单明了的解决scheme。 它使用Web服务器。

每个Web服务器都有能力将用户redirect到HTTP 404的错误页面。要解决此问题,您需要将用户redirect到索引页面。

如果您使用Java基本服务器(tomcat或任何Java应用程序服务器),则解决scheme可能如下所示:

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> 

例:

  • GET http://example.com/about
  • Web服务器抛出HTTP 404,因为这个页面在服务器端不存在
  • 错误页面configuration告诉服务器将index.jsp页面发回给用户
  • 那么JS会在客户端完成其余的工作,因为客户端的url仍然是http://example.com/about

就是这样,没有更多的魔法需要:)

将此添加到webpack.congif.js

 devServer: { historyApiFallback: true } 

如果您在IIS中托pipe; 添加到我的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> 

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

我有同样的问题, 这个解决scheme为我们工作..

背景:

我们在同一台服务器上托pipe多个应用程序。 当我们刷新服务器不知道在哪里寻找我们的特定应用程序的dist文件夹中的索引。 上面的链接将带你到我们的工作…希望这会有所帮助,因为我们已经花了相当多的时间来找出我们的需求的解决scheme。

我们正在使用:

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

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

1)User 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

预执行路由器的Preact解决scheme

与刷新和直接访问

对于那些通过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> ); 

https://jsfiddle.net/developit/gLyL6rbn/

如果您在IIS上托pipe您的反应应用程序,只需添加一个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错误,并且不需要使用哈希历史logging。

如果有人在这里寻找与Laravel的React JS SPA解决scheme。 接受的答案是为什么会出现这种问题的最好解释。 如前所述,您必须configuration客户端和服务器端。 在刀片模板中,包含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' ,它首先加载在服务器端。 由于在服务器端,这个位置没有任何东西,它没有find。 当你有这个可选的路由器,它加载相同的页面,反应路由器也被加载,然后反应加载程序决定显示哪个组件。 希望这可以帮助。

实用的解决方法:

  1. 复制并重命名为joblist.html的原始HTML文件
  2. 使用joblist.html作为pathpath

无需服务器端更改。