我正在使用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);
});

当前回答

服务器端与客户端

首先要理解的是,现在有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 JavaScript 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 only to 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.

所以基本上,当你点击一个链接时,一些JavaScript会在地址栏中操作URL,而不会引起页面刷新,这反过来会导致React路由器在客户端执行页面转换。

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

你的麻烦就从这里开始了。到目前为止,您只需要在服务器的webroot中放置一个静态HTML就可以了。但是,当从服务器请求其他url时,会出现404错误。这些相同的url在客户端工作得很好,因为有React路由器为你做路由,但它们在服务器端失败,除非你让你的服务器理解它们。

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

如果希望http://example.com/about URL在服务器端和客户端都能工作,则需要在服务器端和客户端为其设置路由。这很有道理,对吧?

这就是你的选择开始的地方。解决方案包括完全绕过这个问题(通过一个返回引导HTML的全面路径),以及完全同构的方法(服务器和客户端都运行相同的JavaScript代码)。

完全绕过这个问题:哈希历史

使用哈希历史,而不是浏览器历史,你的关于页面的URL看起来像这样: http://example.com/#/about

散列符号(#)后面的部分不会发送到服务器。因此,服务器只看到http://example.com/,并按预期发送索引页面。React Router将选择#/about部分并显示正确的页面。

缺点:

“丑陋”的url 使用这种方法不可能实现服务器端呈现。就搜索引擎优化(SEO)而言,你的网站由一个单一的页面组成,上面几乎没有任何内容。

全方位

使用这种方法,您确实使用了浏览器历史记录,但只是在服务器上设置了一个将/*发送到index.html的“捕捉器”,有效地为您提供了与使用散列历史记录相同的情况。不过,你确实有干净的url,你可以在以后改进这个方案,而不必使所有用户的收藏夹无效。

缺点:

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

混合动力

在混合方法中,通过为特定的路由添加特定的脚本,您可以扩展“全方位”场景。你可以编写一些简单的PHP脚本,返回包含内容的网站最重要的页面,这样Googlebot至少可以看到你的页面上有什么。

缺点:

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

同构

如果我们使用Node.js作为服务器,那么我们可以在两端运行相同的JavaScript代码呢?现在,我们已经在一个react-router配置中定义了所有的路由,我们不需要复制呈现代码。可以说,这是“圣杯”。如果页面转换发生在客户端上,服务器发送的标记与我们最终得到的标记完全相同。这个解决方案在SEO方面是最优的。

缺点:

服务器必须(能够)运行JavaScript。我曾尝试将Java与Nashorn结合使用,但它并不适合我。实际上,这主要意味着你必须使用基于Node.js的服务器。 许多棘手的环境问题(在服务器端使用窗口等) 陡峭的学习曲线

我应该用哪一种?

选择一个你可以逃避惩罚的。就我个人而言,我认为这是一个非常简单的设置,所以这将是我的最小值。这种设置可以让你随着时间的推移不断改进。如果你已经使用Node.js作为你的服务器平台,我肯定会研究做一个同构应用程序。是的,一开始很难,但一旦你掌握了它,它实际上是一个非常优雅的解决问题的方法。

所以基本上,对我来说,这就是决定因素。如果我的服务器运行在Node.js上,我会采用同构;否则,我会去抓所有的解决方案,只是扩展它(混合解决方案)随着时间的推移和搜索引擎优化的需求。

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

用同构应用应对未来 在ReactJS中创建同构应用的痛苦和快乐 如何实现Node + React同构JavaScript &为什么它很重要

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

创建React应用

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

选择你最完美的React启动项目

我从这些开始:

React同构启动器 React Redux通用热示例

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

祝你好运!

其他回答

使用useEffect读取URL并应用您想要的更改。


例如,一个页面有一个按钮,打开一个滑块。单击按钮时,将向URL中添加滑块的查询参数,并打开滑块。

问题-在刷新页面时打开滑块,加载的页面不显示打开的滑块,即使查询参数存在。

解决方案:添加一个useEffect,读取“slider”查询参数,并运行按钮处理程序。为滑块查询参数添加一个状态变量,并将其包含在useEffect的依赖数组中。

你不需要担心后端请求,假设在滑块组件中有一个。

如果你在IIS上托管React应用程序,只需添加一个web。配置文件包含:

<?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错误,并且不需要使用哈希历史。

如果你正在使用Create React App:

你可以在Create React App页面上找到许多主要托管平台的解决方案。例如,我使用React Router v4和Netlify作为前端代码。它只需要在我的公用文件夹中添加一个文件(“_redirects”),并在该文件中添加一行代码:

/*  /index.html  200

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

在后端使用Express.js,在前端使用React(没有React -create-app)和reach/router,正确的reach/router路由React组件会显示出来,当在地址栏中点击Enter时,菜单链接将被设置为活动样式,例如http://localhost:8050/pages。

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

网络包:

设置代理。这允许任何从端口3000 (React)调用服务器, 包括当按下回车键时在地址栏中获取index.html或任何东西的调用。它还允许调用API路由来获取JSON数据。

比如await axios。Post ('/api/login', {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的任何请求。它只返回dist文件夹中的index.html页面。当然,这个页面有一个更单页的React应用程序。(注意任何其他路由都应该出现在这个上面,在我的情况下,这些是我的API路由。)

反应路线

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

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

React布局构造函数

constructor(props) {
    super(props);

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

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

菜单的代码

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

我们使用Express.js的404处理方法。

// Path to the static React build directory
const frontend = path.join(__dirname, 'react-app/build');

// Map the requests to the static React build directory
app.use('/', express.static(frontend));

// All the unknown requests are redirected to the React SPA
app.use(function (req, res, next) {
    res.sendFile(path.join(frontend, 'index.html'));
});

这招很管用。现场演示是我们的网站。