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

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

我的整个路由器。

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);});
709540 次浏览

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

当你使用React路由器链接组件时,它会阻止浏览器导航并调用过渡到进行客户端导航。你使用的是历史位置,所以它使用HTML5历史API通过模拟地址栏中的新URL来完成导航的错觉。如果你使用的是旧浏览器,这将不起作用。你需要使用HashPlace组件。

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

例如,在Express.js中,它可能看起来像这样:

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

请注意,请求路径正在传递给run。为此,你需要有一个服务器端视图引擎,你可以将渲染的超文本标记语言传递给它。使用renderToString和在服务器上运行React还有许多其他考虑因素。页面在服务器上渲染后,当你的应用程序加载到客户端时,它将再次渲染,根据需要更新服务器端渲染的超文本标记语言。

服务端vs客户端

对此要理解的第一件大事是,现在有2个地方解释URL,而过去只有1个。在过去,当生活很简单的时候,一些用户向服务器发送对http://example.com/about的请求,服务器检查URL的路径部分,确定用户正在请求about页面,然后发回该页面。

使用React路由器提供的客户端路由,事情就不那么简单了。起初,客户端还没有加载任何JavaScript代码。所以第一个请求总是向服务器发出。然后,这将返回一个页面,其中包含加载React和React路由器等所需的脚本标签。只有当这些脚本加载后,第二阶段才开始。在第二阶段,当用户单击“关于我们”导航链接时,例如,URL被更改为仅在本地http://example.com/about(通过历史API成为可能),但没有向服务器发出请求。相反,React路由器在客户端做它的事情,决定渲染哪个React视图,并渲染它。假设您的about页面不需要进行任何REST调用,它已经完成了。您已经从首页转换到关于我们而没有触发任何服务器请求。

因此,基本上,当您单击链接时,一些JavaScript会运行以操作地址栏不会导致页面刷新中的URL,这反过来会导致React路由器执行页面转换在客户端

但是现在考虑一下,如果你在地址栏中复制粘贴URL并通过电子邮件将其发送给朋友会发生什么。你的朋友还没有加载你的网站。换句话说,她仍然在阶段1。她的机器上还没有运行React路由器。所以她的浏览器将成为服务器请求http://example.com/about

这就是你的麻烦开始的地方。到目前为止,你可以只在服务器的webroot上放置一个静态的超文本标记语言。但这会给所有其他URL当从服务器请求时带来404错误。这些相同的URL工作正常在客户端,因为React路由器正在为你做路由,但是它们失败了在服务端,除非你让你的服务器理解它们。

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

如果您希望http://example.com/about URL同时在服务器端和客户端工作,您需要在服务器端和客户端为它设置路由。这是有道理的,对吧?

这就是你的选择开始的地方。解决方案的范围从完全绕过问题,通过返回引导超文本标记语言的包罗万象的路线,到服务器和客户端运行相同JavaScript代码的完全同构方法。

完全绕过问题:哈希历史

使用哈希历史而不是浏览器历史,您的about页面的URL将如下所示:http://example.com/#/about

哈希(#)符号后的部分不会发送到服务器。因此服务器只看到http://example.com/并按预期发送索引页面。React Rout将获取#/about部分并显示正确的页面。

缺点

  • “丑陋”URL
  • 使用这种方法无法进行服务器端渲染。就搜索引擎优化(SEO)而言,您的网站由一个几乎没有任何内容的页面组成。

包罗万象

使用这种方法,您确实使用了浏览器历史记录,但只需在服务器上设置一个发送/*index.html的包罗万象,有效地为您提供与哈希历史记录大致相同的情况。但是,您确实有干净的URL,并且您可以稍后改进此方案,而无需使所有用户的收藏夹无效。

缺点

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

混合

在混合方法中,你可以通过为特定路由添加特定脚本来扩展包罗万象的场景。你可以制作一些简单的PHP脚本来返回包含内容的网站最重要的页面,这样Googlebot至少可以看到你页面上的内容。

缺点

  • 设置起来就更复杂了
  • 只有好的SEO对于那些路线你给特殊待遇
  • 在服务器和客户端上呈现内容的复制代码

同构

如果我们使用Node.js作为我们的服务器,这样我们就可以在两端运行相同的JavaScript代码怎么办?现在,我们已经在一个反应路由器配置中定义了所有路由,我们不需要复制我们的渲染代码。可以说,这是“圣杯”。服务器发送的标记与我们在客户端发生页面转换时发送的标记完全相同。这个解决方案在SEO方面是最佳的。

缺点

  • 服务器必须(能够)运行JavaScript。我已经尝试过Java结合Nashorn,但它不适合我。在实践中,这主要意味着您必须使用基于Node.js的服务器。
  • 许多棘手的环境问题(在服务器端使用window等)
  • 陡峭的学习曲线

我应该用哪个?

选择一个你可以逃脱的。就个人而言,我认为万能的设置足够简单,所以这将是我的最低要求。这种设置允许你随着时间的推移改进事情。如果你已经在使用Node.js作为你的服务器平台,我肯定会研究做一个同构应用程序。是的,一开始很难,但一旦你掌握了诀窍,它实际上是一个非常优雅的问题解决方案。

所以基本上,对我来说,这将是决定性因素。如果我的服务器在Node.js上运行,我会去同构;否则,我会选择Catch-all解决方案,并随着时间的推移和SEO需求的需要对其进行扩展(混合解决方案)。

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

此外,为了让你开始,我建议查看一些入门工具包。选择一个与你的技术堆栈选择相匹配的工具包(记住,React只是MVC中的V,你需要更多的东西来构建一个完整的应用程序)。从查看Facebook自己发布的那个开始:

或者从社区中选择一个。现在有一个不错的网站试图索引所有这些:

我从这些开始:

目前,我正在使用一个自制的通用渲染版本,它的灵感来自上面的两个入门工具包,但它们现在已经过时了。

祝你好运与你的追求!

如果您确实有回退到index.html,请确保在index.html文件中包含以下内容:

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

这可能因项目而异。

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

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

历史ApiFallback为我解决了这个问题。现在路由工作正常,我可以刷新页面或直接输入URL。无需担心Node.js服务器上的变通方法。这个答案显然仅在您使用Webpack时有效。

请参阅我对React-router 2.0浏览器的回答刷新时历史记录不起作用了解为什么这是必要的更详细的原因。

Webpack Dev Server有一个选项可以启用此功能。打开package.json并添加--history-api-fallback。这个方法对我很有效

react-router-的教程

我还没有使用服务端渲染,但我遇到了与OP相同的问题,其中Link在大多数情况下似乎工作正常,但当我有参数时失败了。我将在这里记录我的解决方案,看看它是否对任何人都有帮助。

我的主要JSX内容包含:

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

这对于第一个匹配的链接很有效,但是当嵌套在该模型的详细信息页面上的<Link>表达式中的: id发生变化时,浏览器栏中的URL会发生变化,但页面的内容最初并没有改变以反映链接的模型。

问题是我使用props.params.id设置了componentDidMount中的模型。组件只挂载一次,所以这意味着第一个模型是粘贴在页面上的模型,随后的链接会更改道具,但页面看起来不变。

componentDidMountcomponentWillReceiveProps(基于下一个props)中将模型设置为组件状态可以解决问题,并且页面内容会更改以反映所需的模型。

如果您使用Apache作为Web服务器,您可以将其插入到.htaccess文件中:

<IfModule mod_rewrite.c>RewriteEngine OnRewriteBase /RewriteRule ^index\.html$ - [L]RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-lRewriteRule . /index.html [L]</IfModule>

我正在使用react: "^16.12.0"react-router: "^5.1.2"这种方法是万能的,可能是让你开始的最简单的方法。

这是一个简单、清晰和更好的解决方案。如果您使用Web服务器,它会起作用。

在HTTP 404的情况下,每个Web服务器都能够将用户重定向到错误页面。要解决此问题,您需要将用户重定向到索引页面。

如果您使用Java基本服务器(Tomcat或任何Java应用程序服务器),解决方案可能如下:

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>

示例:

  • 获取http://example.com/about
  • Web服务器抛出HTTP 404,因为此页面在服务器端不存在
  • 错误页面配置告诉服务器将index.jsp页面发送回用户
  • 然后JavaScript将在客户端完成其余的工作,因为客户端的URL仍然是http://example.com/about

就是这样。不再需要魔法了:)

这可以解决你的问题。

我在生产模式下的React应用程序中也遇到了同样的问题。

解决方案1.将路由历史记录更改为“hashHistory”而不是BrowserHistory

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

现在使用命令构建应用程序

sudo npm run build

然后将构建文件夹放在您的var/www/文件夹中。现在,在每个URL中添加#标签后,应用程序运行良好。喜欢

localhost/#/homelocalhost/#/aboutus

解决方案2:没有#标签,使用BrowserHistory,

在路由器中设置您的历史记录={BrowserHistory}。现在使用sudo npm run build构建它。

您需要创建“conf”文件来解决404未找到页面。conf文件应该是这样的。

打开您的终端键入以下命令

cd /etc/apache2/sites-availablelsnano sample.conf

在其中添加以下内容。

<VirtualHost *:80>ServerAdmin admin@0.0.0.0ServerName 0.0.0.0ServerAlias 0.0.0.0DocumentRoot /var/www/html/
ErrorLog ${APACHE_LOG_DIR}/error.logCustomLog ${APACHE_LOG_DIR}/access.log combined<Directory "/var/www/html/">Options Indexes FollowSymLinksAllowOverride allRequire all granted</Directory></VirtualHost>

现在您需要使用以下命令启用sample.conf文件:

cd /etc/apache2/sites-availablesudo a2ensite sample.conf

然后它会要求您重新加载Apache服务器,使用

sudo service apache2 reload or restart

然后打开localhost/build文件夹并添加. htaccess文件,其中包含以下内容。

RewriteEngine OnRewriteBase /RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-lRewriteRule ^.*$ / [L,QSA]

现在该应用程序正常工作。

注意:将0.0.0.0IP地址更改为本地IP地址。

对于React路由器V4用户:

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

<Router history={hashHistory} >

在V4中不起作用。请改用HashRouter

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

参考:Hash路由器

如果您在IIS中托管:将此添加到我的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>

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

我有同样的问题,这个解决方案为我们工作…

背景:

我们在同一台服务器上托管多个应用程序。当我们刷新服务器时,它不知道在该特定应用程序的目标文件夹中的哪里查找我们的索引。上面的链接将带您找到对我们有用的方法…

我们正在使用:

文件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文件:

/* 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文件:

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应用程序页面上找到这里。例如,我使用React路由器v4和Netlify作为我的前端代码。只需将一个文件添加到我的公用文件夹(“_redirects”)和该文件中的一行代码:

/*  /index.html  200

现在我的网站在进入浏览器或有人点击刷新时正确呈现像mysite.com/pricing这样的路径。

生产堆栈: React, React Rout v4, BrowswerRout,Express.js, Nginx

  1. 漂亮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. 捆绑包webpackwebpack -p

  4. 运行nodemon server.jsnode server.js

您可能希望让nginx在服务器块中处理此问题并忽略步骤2:

location / {try_files $uri /index.html;}

我刚才用创建React应用做了一个网站,这里也有同样的问题。

我使用react-router-dom包中的BrowserRouting。我在nginx服务器上运行,并将以下内容添加到/etc/nginx/yourconfig.conf为我解决了问题:

location / {if (!-e $request_filename){rewrite ^(.*)$ /index.html break;}}

这对应于在运行apache的情况下将以下内容添加到.htaccess

Options -MultiViewsRewriteEngine OnRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^ index.html [QSA,L]

这似乎也是Facebook自己提出的解决方案,可以找到这里

将其添加到webpack.config.js

devServer: {historyApiFallback: true}

使用preact-router的Preact解决方案

适用于刷新和直接访问

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

jsfiddle

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

使用Laravel在React中为JavaScriptSPA提供解决方案

接受的答案是对为什么会发生此类问题的最佳解释。正如已经解释的那样,您必须配置客户端和服务器端。

在您的刀片模板中,包含JavaScript捆绑文件,确保使用url外观,如下所示:

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

发生的问题是首先加载刀片模板,然后加载React路由器。因此,当您加载'/setting-alerts'时,它会加载超文本标记语言内容和JavaScript代码。

但是当你加载'/setting-alerts/about'时,它首先在服务器端加载。因为它在服务器端,所以这个位置上没有任何东西,它返回未找到。当你有那个可选的路由器时,它加载同一个页面,反应路由器也被加载,然后React加载器决定显示哪个组件。

如果您通过AWS Static托管React应用程序S3

这个问题是由CloudFront以403访问被拒绝消息响应自己提出的,因为它期望/some/other/path存在于我的S3文件夹中,但该路径仅在React的反应路由器路由中内部存在。

解决方案是设置分配错误页面规则。转到CloudFront设置并选择您的分配。接下来,转到“错误页面”选项卡。单击“创建自定义错误响应”并添加403的条目,因为这是我们得到的错误状态代码。

响应页路径设置为/index.html,状态码设置为200。

最终结果的简单性让我感到惊讶。索引页面被提供,但URL保留在浏览器中,因此一旦React应用程序加载,它就会检测URL路径并导航到所需的路由。

错误页403规则

当您在刷新DOM组件后无法获得403错误时,这很简单。

只需在您的webpack配置中添加这一行,“历史ApiFallback:true”。这节省了我一整天的时间。

如果您在后端使用Express.js或其他一些框架,您可以添加如下类似的配置,并查看配置中的Webpack公共路径。如果您使用的是Browser路由器,即使重新加载也应该可以正常工作。

expressApp.get('/*', (request, response) => {response.sendFile(path.join(__dirname, '../public/index.html'));});

index.html文件的head中,添加以下内容:

<base href="/"><!-- This must come before the CSS and JavaScript code -->

然后,当使用webpack开发服务器运行时,使用此命令。

webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback

--history-api-fallback是最重要的部分

对于那些正在使用IIS 10的人来说,这是你应该做的事情。

确保你使用的是浏览器历史。作为参考,我会给出路由的代码,但这并不重要。重要的是下面组件代码之后的下一步:

class App extends Component {render() {return (<Router history={browserHistory}><div><Root><Switch><Route exact path={"/"} component={Home} /><Route path={"/home"} component={Home} /><Route path={"/createnewproject"} component={CreateNewProject} /><Route path={"/projects"} component={Projects} /><Route path="*" component={NotFoundRoute} /></Switch></Root></div></Router>)}}render (<App />, window.document.getElementById("app"));

由于问题是IIS接收来自客户端浏览器的请求,它会将URL解释为请求页面,然后返回404页面,因为没有任何可用页面。执行以下操作:

  1. 打开IIS
  2. 展开服务器,然后打开网站文件夹
  3. 点击网站/应用程序
  4. 转到错误页面
  5. 在列表中打开404错误状态项
  6. 而不是选项“从静态文件插入内容到错误响应中”,将其更改为“在此站点上执行URL”并将“/”斜杠值添加到URL。

现在它将工作得很好。

在此输入图片描述

在此输入图片描述

尝试使用以下代码在公用文件夹中添加“. htaccess”文件。

RewriteEngine OnRewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -dRewriteRule ^ - [L]
RewriteRule ^ /index.html [L]

如果您使用的是Firebase,您所要做的就是确保您在应用程序根目录(在托管部分)的firebase.json文件中拥有重写属性。

例如:

{"hosting": {"rewrites": [{"source":"**","destination": "/index.html"}]}}

关于该主题的进一步阅读:

我喜欢这种处理方式。尝试添加:在服务器端使用Your SPAPageRoute/*来摆脱这个问题。

我采用了这种方法,因为即使是原生HTML5历史API也不支持页面刷新时的正确重定向(据我所知)。

注意:选择的答案已经解决了这个问题,但我试图更具体。

快速路线

测试-历史API

修复刷新或直接调用URL时的“无法获取 /URL”错误。

配置您的webpack.config.js以期望像这样的给定链接路由。

module.exports = {entry: './app/index.js',output: {path: path.join(__dirname, '/bundle'),filename: 'index_bundle.js',publicPath: '/'},

我使用React路由器(Apache)找到了我的SPA的解决方案。只需将其添加到文件. htaccess中:

<IfModule mod_rewrite.c>
RewriteEngine OnRewriteBase /RewriteRule ^index\.html$ - [L]RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-lRewriteRule . /index.html [L]
</IfModule>

来源:React路由器的Apache配置

我正在使用webpack,我遇到了同样的问题。

解决方案:

在您的server.js文件中:

const express = require('express');const app = express();
app.use(express.static(path.resolve(__dirname, '../dist')));app.get('*', function (req, res) {res.sendFile(path.resolve(__dirname, '../dist/index.html'));// res.end();});

为什么刷新后我的应用程序不呈现?

当我使用ASP.NET核心时,这样的东西帮助了我:

public class HomeController : Controller{public IActionResult Index(){var url = Request.Path + Request.QueryString;return App(url);}
[Route("App")]public IActionResult App(string url){return View("/wwwroot/app/build/index.html");}

}

基本上在ASP.NETMVC端,所有不匹配的路由都将落入startup.cs中指定的Home/Index。在Index中,可以获取原始请求URL并将其传递到任何需要的地方。

文件startup.cs

app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(name: "spa-fallback",defaults: new { controller = "Home", action = "Index" });});

Joshua Dyck的回答添加更多信息。

如果您使用的是Firebase并希望同时使用根路由和子目录路由,则需要在firebase.json中添加以下代码:

{"hosting": {"rewrites": [{"source": "*","destination": "/index.html"},{"source": "/subdirectory/**","destination": "/subdirectory/index.html"}]}}

示例:

您正在为客户构建一个网站。您希望网站的所有者在https://your.domain.com/management中添加信息,而网站的用户将导航到https://your.domain.com

在这种情况下,您的firebase.json文件将如下所示:

{"hosting": {"rewrites": [{"source": "*","destination": "/index.html"},{"source": "/management/**","destination": "/management/index.html"}]}}

假设您有以下首页路由定义:

<Route exact path="/" render={routeProps => (<Home routeProps={routeProps}/>)}/>
{/* Optional catch-all router */}<Route render={routeProps => (<div><h4>404 not found</h4></div>)}/>

在您的首页组件上,您可以在ComponentWillMount事件中拦截请求,

const searchPath = this.props.routeProps.location.search;
if (searchPath){this.props.routeProps.history.push("/" + searchPath.replace("?",""));}else{/*.... originally Home event */}

现在,您可以请求/? joblist,而不是在URL上调用/joblist组件将自动将请求重定向到/joblist(请注意路径中的额外问号)。

如果你来这里并且你使用apache并且没有. htaccess文件,这是一个对我有用的配置文件:

sites-enabled/somedomain.com.conf
<VirtualHost *:80>ServerName somedomain.comServerAlias *.somedomain.comDocumentRoot /www/somedomain.com/build
RewriteEngine OnRewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteRule . /www/somedomain.com/build/index.html [L,NC,QSA]
</VirtualHost>

这是我发现的一个前端解决方法,不需要修改服务器上的任何内容。

假设你的网站mysite.com,你有一个React路由mysite.com/about.在index.js中,您可以在其中挂载顶级组件,您可以放置另一个路由器,例如:

ReactDOM.render(<Router><div><Route exact path="/" component={Home} /><Route exact path="/about"render={(props) => <Home {...props} refreshRout={"/about"}/>}/></div></Router>,

我假设您的原始路由器位于虚拟DOM中顶级组件下方的某个地方。如果您使用Django,您还必须在. urls中捕获url,例如:

urlpatterns = [path('about/', views.index),]

但是,这将取决于您使用的后端。请求mysite/about将使您进入index.js(您可以在其中挂载顶级组件),您可以在其中使用Route的渲染道具,而不是组件道具,并将'/about'作为道具传递给,在本例中,Home组件。

在Home中,无论是在组件迪迪山()还是使用效果()挂钩中,执行:

useEffect() {//check that this.props.refreshRoute actually exists before executing the//following linethis.props.history.replace(this.props.refreshRoute);}

我假设您的Home组件正在呈现如下内容:

<Router><Route exact path="/" component={SomeComponent} /><Route path="/about" component={AboutComponent} /></Router>

归功于(将props传递给React路由器渲染的组件),了解如何将道具传递给Routes中的组件。

我们使用Express.js404处理方法

// Path to the static React build directoryconst frontend = path.join(__dirname, 'react-app/build');
// Map the requests to the static React build directoryapp.use('/', express.static(frontend));
// All the unknown requests are redirected to the React SPAapp.use(function (req, res, next) {res.sendFile(path.join(frontend, 'index.html'));});

它就像一个魅力。现场演示是我们的网站

如果您使用的是“create-react-app”命令,

要生成React应用程序,那么package.json文件需要对浏览器中正常运行的生产构建React SPA进行一次更改。打开文件package.json并将以下代码段添加到其中,

"start": "webpack-dev-server --inline --content-base . --history-api-fallback"

在这里,最重要的部分是启用历史API回调的“--历史API回调”。

如果你使用Spring或任何其他后端API,有时你会得到404错误。所以在这种情况下,你需要在后端有一个控制器来将任何请求(你想要的)转发到index.html文件以由react-router处理。下面演示了一个使用Spring编写的示例控制器。

@Controllerpublic class ForwardingController {@RequestMapping("/<any end point name>/{path:[^\\.]+}/**")public String forward(HttpServletRequest httpServletRequest) {return "forward:/";}}

例如,如果我们将后端APIREST端点作为“abc”(http://localhost:8080/abc/**),则到达该端点的任何请求都将重定向到React应用程序(index.html文件),然后react-router将处理该请求。

使用HashRouter对我来说也适用于Redux。只需替换:

import {Router //replace Router} from "react-router-dom";
ReactDOM.render(<LocaleProvider locale={enUS}><Provider store={Store}><Router history={history}> // Replace here saying Router<Layout/></Router></Provider></LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

有:

import {HashRouter // Replaced with HashRouter} from "react-router-dom";
ReactDOM.render(<LocaleProvider locale={enUS}><Provider store={Store}><HashRouter history={history}> //replaced with HashRouter<Layout/></HashRouter></Provider></LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

我通过更改文件webpack.config.js解决了这个问题。

我的新配置如下:

之前

output: {path: path.join(__dirname, '/build/static/js'),filename: 'index.js'},

devServer: {port: 3000}

之后

output: {path: path.join(__dirname, '/build/static/js'),filename: 'index.js',publicPath: '/'},

devServer: {historyApiFallback: true,port: 3000}

我正在使用。NET Core 3.1并刚刚添加了扩展MapFallbackToController

文件Startup.cs

    app.UseEndpoints(endpoints =>{endpoints.MapControllerRoute(name: "default",pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapFallbackToController("Index", "Home");});

请求数据的另一种方式,即使您立即指向URL,也是让每个组件都有一个调用最后一个参数的方法,例如 /about/测试

然后到您的State Provider,您拥有连接到要请求数据的组件的函数。

如果您在Google水桶上运行它,简单的解决方案是考虑“index.html”错误(404未找到)页面。

这样做:

  1. 在存储桶列表中,找到您创建的存储桶。
  2. 单击与存储桶关联的存储桶溢出菜单(…)并选择编辑网站配置。
  3. 在网站配置对话框中,指定主页面作为错误页面。

如果尝试从IIS虚拟目录(不是网站的根)提供React应用程序:

设置重定向时,'/'不会自行工作。对我来说,它也需要那里的虚拟目录名称。这是我的Web配置的样子:

<?xml version="1.0" encoding="UTF-8"?><configuration><system.webServer><defaultDocument><files><remove value="default.aspx" /><remove value="iisstart.htm" /><remove value="index.htm" /><remove value="Default.asp" /><remove value="Default.htm" /></files></defaultDocument><rewrite><rules><rule name="React Routes" stopProcessing="true"><match url=".*" /><conditions logicalGrouping="MatchAll"><add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /><add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /><add input="{REQUEST_URI}" pattern="^/(api)" negate="true" /></conditions><action type="Rewrite" url="/YOURVIRTUALDIRECTORYNAME/" /></rule></rules></rewrite><directoryBrowse enabled="false" /><httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL"><remove statusCode="500" subStatusCode="100" /><remove statusCode="500" subStatusCode="-1" /><remove statusCode="404" subStatusCode="-1" /><remove statusCode="403" subStatusCode="18" /><error statusCode="403" subStatusCode="18" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" /><error statusCode="404" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" /><error statusCode="500" prefixLanguageFilePath="" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" /><error statusCode="500" subStatusCode="100" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" /></httpErrors></system.webServer></configuration>

除了web.config文件之外,React应用程序本身还需要一些更改:

在文件package.json中,您需要添加一个“主页”条目:

{"name": "sicon.react.crm","version": "0.1.0","private": true,"homepage": "/YOURVIRTUALDIRECTORYNAME/","dependencies": {...

我将basename添加到我的浏览器历史记录对象中,我将其传递到路由器以访问历史记录:

import  {createBrowserHistory } from 'history';
export default createBrowserHistory({//Pass the public URL as the base name for the router basename: process.env.PUBLIC_URL});

我还在文件App.js中的React路由器上添加了此属性:

<Router history={history} basename={process.env.PUBLIC_URL}>

最后,在文件index.html中,我在“title”标签上方添加了以下选项卡:

<base href="%PUBLIC_URL%/">

可能有些步骤不需要,但这似乎已经完成了我的工作。我不知道如何将其设置为在站点根目录或虚拟目录中运行而无需重新编译,因为据我所知,package.json中的主页在构建后无法交换。

在后端使用Express.js,在前端使用到达/路由器(没有应用创建),正确的到达/路由器路由React组件被显示,菜单链接被设置为活动样式时,在地址栏中点击输入,例如,http://localhost:8050/pages.

请查看下面的内容,或者直接访问我的存储库https://github.com/nickjohngray/staticbackeditor。所有代码都在那里。

Webpack

设置代理。这允许来自端口3000(React)的任何调用调用服务器,包括在命中输入键时调用获取index.html或地址栏中的任何内容。它还允许调用API路由以获取JSON数据。

类似等待axios.post('/api/log',{email, pwd}):

devServer: {port: 3000,open: true,proxy: {'/': 'http://localhost:8050',}}

设置Express.js路线

app.get('*', (req, res) => {console.log('sending index.html')res.sendFile(path.resolve('dist', 'index.html'))
});

这将匹配来自React的任何请求。它只是返回index.html页面,它在我的dist文件夹中。当然,这个页面有一个更单页的React应用程序。(注意,任何其他路由都应该出现在上面,在我的情况下,这些是我的API路由。)

React Routes

<Router><Home path="/" /><Pages path="pages"/><ErrorPage path="error"/><Products path="products"/><NotFound default /></Router>

这些路由在我的Layout组件中定义,当路径匹配时将加载相应的组件。

React Layout构造函数

constructor(props) {super(props);
this.props.changeURL({URL: globalHistory.location.pathname});}

Layout构造函数在加载后立即被调用。在这里,我调用我的菜单监听的redux操作change eURL,因此它可以突出显示正确的菜单项,如下所示:

菜单代码

<nav>{this.state.links.map( (link) =><Link className={this.getActiveLinkClassName(link.path) } to={link.path}>{link.name}</Link>)}</nav>

我正在使用React.js+Webpack模式。我在package.json文件中添加了--history-api-fallback参数。然后页面刷新正常工作。

每次更改代码时,网页都会自动刷新。

"scripts": {"start": "rimraf build && cross-env NODE_ENV='development' webpack --mode development && cross-env NODE_ENV=development webpack-dev-server --history-api-fallback",...}

前面的答案并没有解决您希望使用带有代理通行证的浏览器路由器以及无法使用root的问题。

对我来说,解决方案很简单。

假设您有一个指向某个端口的URL。

location / {proxy_pass http://127.0.0.1:30002/;proxy_set_header    Host            $host;port_in_redirect    off;}

现在因为浏览器路由器,子路径被破坏了。然而,你知道子路径是什么。

解决方案是什么?对于子路径/contact

# Just copy paste.location /contact/ {proxy_pass http://127.0.0.1:30002/;proxy_set_header    Host            $host;}

我尝试过的其他方法都不管用,但这个简单的修复方法管用。

在我的情况下,当我在其中使用参数时,URL没有加载。

作为快速修复,我添加了<base href="<yourdomain/IP>"></base>在构建文件夹中index.html文件的标记下。</p><p>这只是解决了我的问题。</p></div> </div> </div> </div> </div> <div class="comment"> <div class="avatar"> <a href="#"> <img src="/storage/images/avatar.png" alt=""> </a> </div> <div class="content"> <div class="comment-header" style=""> <div class="meta"> <a class="author" href="javascript:void(0);">小开</a> <div class="metadata"></div> </div> </div> <div class="text comment-body markdown-reply" style=""> <div class="reply-detail"> <div class="why"><p>如果您使用<a href="https://en.wikipedia.org/wiki/Nginx" rel="nofollow noreferrer">nginx</a>托管并需要快速修复…</p><p>将以下行添加到<em>位置</em>块内的nginx配置中:</p><pre><code>location / {try_files $uri /index.html;}</code></pre></div> </div> </div> </div> </div> <div class="comment"> <div class="avatar"> <a href="#"> <img src="/storage/images/avatar.png" alt=""> </a> </div> <div class="content"> <div class="comment-header" style=""> <div class="meta"> <a class="author" href="javascript:void(0);">小开</a> <div class="metadata"></div> </div> </div> <div class="text comment-body markdown-reply" style=""> <div class="reply-detail"> <div class="why"><p>当我使用React作为前端和<code>react-router-dom</code>作为路由时,我在<a href="https://en.wikipedia.org/wiki/电子_(software_framework)" rel="nofollow noreferrer">电子</a>中遇到了这个问题。</p><p>我用<code>HashRouter</code>替换了<code>BrowserRouter</code>,它是固定的。</p><p>这里有一个简单的例子:</p><pre><code>import {HashRouter as Router,Switch,Route,} from "react-router-dom";</code></pre></div> </div> </div> </div> </div> <div class="comment"> <div class="avatar"> <a href="#"> <img src="/storage/images/avatar.png" alt=""> </a> </div> <div class="content"> <div class="comment-header" style=""> <div class="meta"> <a class="author" href="javascript:void(0);">小开</a> <div class="metadata"></div> </div> </div> <div class="text comment-body markdown-reply" style=""> <div class="reply-detail"> <div class="why"><p>Hash路由器将是一个简单的实现,</p><pre><code>import {HashRouter as Router,Switch,Route,Link} from 'react-router-dom'; function App() {return (<Router><Switch><Route path="/" exact component={InitialComponent} /><Route path="/some" exact component={SomeOtherComponent} /></Switch></Router>);}</code></pre><p>在浏览器中会是这样的-<strong>超文本传输协议:localhost:3000/#/,<strong>超文本传输协议:localhost:3000/#/some</strong></p></div> </div> </div> </div> </div> <div class="comment"> <div class="avatar"> <a href="#"> <img src="/storage/images/avatar.png" alt=""> </a> </div> <div class="content"> <div class="comment-header" style=""> <div class="meta"> <a class="author" href="javascript:void(0);">小开</a> <div class="metadata"></div> </div> </div> <div class="text comment-body markdown-reply" style=""> <div class="reply-detail"> <div class="why"><p>您可以为您的React应用程序使用Vercel的托管,并使用BrowserRouting在您的React应用程序中使用相同的旧路由方式。</p><p>您需要在项目的根目录中添加一个<em>vercel.json</em>文件并将以下代码添加到其中:</p><pre><code>{"rewrites": [{"source": "/((?!api/.*).*)","destination": "/index.html"}]}</code></pre><p>这工作得很好。</p></div> </div> </div> </div> </div> <div class="comment"> <div class="avatar"> <a href="#"> <img src="/storage/images/avatar.png" alt=""> </a> </div> <div class="content"> <div class="comment-header" style=""> <div class="meta"> <a class="author" href="javascript:void(0);">小开</a> <div class="metadata"></div> </div> </div> <div class="text comment-body markdown-reply" style=""> <div class="reply-detail"> <div class="why"><p>我正在使用<strong>ASP.NET核心</strong>和<strong>React</strong>。正式生产环境中手动路由和刷新路由问题的解决方案是在ASP.NET核心的<strong>主要项目</strong>根目录中创建<code>web.config</code>文件,该文件将在正式服上配置路由。</p><p>文件在项目中的位置:</p><p><a href="https://i.stack.imgur.com/SBc06.png"rel="不关注"><img src="https://i.stack.imgur.com/SBc06.png"alt="输入图片描述"/></a></p><p><code>web.config</code>文件的内容:</p><pre><code><?xml version="1.0" encoding="UTF-8"?><configuration><system.webServer><rewrite><rules><rule name="Rewrite Text Requests" stopProcessing="true"><match url=".*" /><conditions><add input="{HTTP_METHOD}" pattern="^GET$" /><add input="{HTTP_ACCEPT}" pattern="^text/html" /><add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /></conditions><action type="Rewrite" url="/index.html" /></rule></rules></rewrite></system.webServer></configuration></code></pre></div> </div> </div> </div> </div> <div class="comment"> <div class="avatar"> <a href="#"> <img src="/storage/images/avatar.png" alt=""> </a> </div> <div class="content"> <div class="comment-header" style=""> <div class="meta"> <a class="author" href="javascript:void(0);">小开</a> <div class="metadata"></div> </div> </div> <div class="text comment-body markdown-reply" style=""> <div class="reply-detail"> <div class="why"><p>通过在我的<a href="https://en.wikipedia.org/wiki/Nginx" rel="nofollow noreferrer">nginx</a>配置中进行以下简单更改,我能够克服硬刷新Web应用程序中断和手动URL键入Web应用程序中断。</p><ul><li>React版本:17.0.2</li><li>Web服务器:nginx</li><li>操作系统:<a href="https://en.wikipedia.org/wiki/Ubuntu_version_history#Ubuntu_20.04_LTS_(Focal_Fossa)" rel="nofollow noreferrer">Ubuntu Server 20.04</a>(焦点Fossa)</li></ul><p><strong>之前</strong></p><pre><code>location / {try_files $uri $uri/ =404;}</code></pre><p><strong>之后</strong></p><pre><code>location / {try_files $uri /index.html;}</code></pre><p>可能有其他的解决方案,但这对我来说真的是快速和节省时间。</p></div> </div> </div> </div> </div> <div class="comment"> <div class="avatar"> <a href="#"> <img src="/storage/images/avatar.png" alt=""> </a> </div> <div class="content"> <div class="comment-header" style=""> <div class="meta"> <a class="author" href="javascript:void(0);">小开</a> <div class="metadata"></div> </div> </div> <div class="text comment-body markdown-reply" style=""> <div class="reply-detail"> <div class="why"><p>当我使用apache(Httpd)服务器时,我遇到了同样的问题。我以这种方式解决了咆哮问题,并为我100%工作。</p><p><strong>step-1:</strong></p><ul><li>/etc/httpd/conf/httpd.conf/新版本转到etc/apache2/apache2.conf</li><li>将AllowOverrive无更改为AllowOverriveAll。</li><li>重新启动apache服务器。</li></ul><p><strong>step-2:</strong></p><p>构建后,将. htaccess文件放入根文件夹。</p><pre><code>Options -MultiViewsRewriteEngine OnRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^ index.html [QSA,L]</code></pre></div> </div> </div> </div> </div> </div> </div> <br/> <div class="reply clearfix" style="margin-left: 60px;background: #fff;"> <input type="hidden" value=""> <form action="https://kaijuan.co/qa/1305/challenge" method="post" id="J_quiz"> <input type="hidden" name="_token" value="9SpYSQao6GY0XgOPWZApLtkvbbDTWhDQPpbodCyq"> <div class="answer"> <textarea name="why" id="answerEditor" type="text" class="form-control mono wmd-input tabIndent" rows="4" placeholder="请登录后挑战答题查看答案 " style="background-position: right top; background-repeat: no-repeat;"></textarea> </div> <div class="submit-reply"> <div class="float-right"> <a id="answerIt" data-id="1010000019487302" class="J_submit_quiz btn btn-lg btn-success ml20 disabled ">提交答案</a> </div> </div> </form> </div> </div> </div> </div> </div> <footer class="footer"> <div class="container"> <p class="float-left"> <a href="https://kaijuan.co/disclaimer">使用前必读</a> </p> <p class="float-left ml-3"> <a href="https://support.qq.com/products/151442" rel="noopener nofollow noindex">意见 & 问题反馈</a> </p> <p class="float-left ml-3"> <a href="//geekdaxue.co" rel="noopener">极客学习笔记</a> </p> <p class="float-right"><a target="_blank" rel="noopener" href="//beian.miit.gov.cn">ICP 证:鄂ICP备15004824号-2</a></p> <p class="float-right"><a rel="noopener" href="/links" class="mr-2">友情链接</a></p> </div> </footer> </div> <!-- JS 脚本 --> <script src="/js/app.js?id=3f8b21f422794e9c60b62d0a2d1e0c98"></script> <script> // $(document).ready(function () { // //提交试卷 // $('.J_submit_quiz').on('click', function() { // $('#J_quiz').submit(); // }); // }); // (window.slotbydup = window.slotbydup || []).push({ // id: "u6019456", // container: "_6palcc9e09a", // async: true // }); </script> <!-- 多条广告如下脚本只需引入一次 --> <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3161682431818340" crossorigin="anonymous"></script> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> </body> </html>