当手动刷新或写入时,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为我工作。
这可以解决你的问题
在生产模式下,我也遇到了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.id
在componentDidMount
设置模型。 该组件只安装一次,这意味着第一个模型是粘贴在页面上的模型,后续的链接改变了道具,但保持页面不变。
在componentDidMount
和componentWillReceiveProps
(它基于下一个道具)中,在组件状态中设置模型可以解决问题,并且页面内容会发生变化以反映所需的模型。
如果您正在使用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.js
或node 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> );
如果您在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。 当你有这个可选的路由器,它加载相同的页面,反应路由器也被加载,然后反应加载程序决定显示哪个组件。 希望这可以帮助。
实用的解决方法:
- 复制并重命名为joblist.html的原始HTML文件
- 使用joblist.html作为pathpath
无需服务器端更改。