React Routing 在本地机器上工作,但在 Heroku 上不工作

Posted

技术标签:

【中文标题】React Routing 在本地机器上工作,但在 Heroku 上不工作【英文标题】:React Routing works in local machine but not Heroku 【发布时间】:2017-06-05 23:01:44 【问题描述】:

React/react router/heroku 问题在这里(它可能是 heroku 失败的地方)。

我正在关注这个精彩的教程:https://medium.com/@patriciolpezjuri/using-create-react-app-with-react-router-express-js-8fa658bf892d#.y77yjte2j 一切正常,直到我将它发布到 heroku 并尝试导航到 https://appname.herokuapp.com/about 并且我收到 404 Not Found/nginx 错误。当然,根据教程,它应该显示一个关于页面。

底线:React 路由器无法在 heroku 上运行,我不知道为什么

我已尝试按照以下建议修改我的 server/app.js 文件:React routes are not working in facebook's create-react-app build

// server/app.js
const express = require('express');
const morgan = require('morgan');
const path = require('path');

const app = express();

console.log('hi from /src/server.js')
// Setup logger
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms'));

// Serve static assets
app.use(express.static(path.resolve(__dirname, '..', 'build')));

// Always return the main index.html, so react-router render the route in the client

app.get('/about', (req, res) => 

  console.log('hi from app.get.about')
  console.log(req)
  console.log(res)
  res.sendFile(path.resolve(__dirname, '..', 'build', 'index.html'));
);
app.get('/*', (req, res) => 

  console.log('hi from app.get')
  console.log(req)
  console.log(res)
  res.sendFile(path.resolve(__dirname, '..', 'build', 'index.html'));
);


module.exports = app;

但它不起作用,也不会在控制台中记录任何内容:

2017-01-20T21:03:47.438140+00:00 heroku[web.1]: Starting process with command `bin/boot`
2017-01-20T21:03:49.540005+00:00 app[web.1]: Injecting runtime env into /app/build/static/js/main.242e967b.js (from .profile.d/inject_react_app_env.sh)
2017-01-20T21:03:49.695317+00:00 app[web.1]: Starting log redirection...
2017-01-20T21:03:49.695899+00:00 app[web.1]: Starting nginx...
2017-01-20T21:03:51.108255+00:00 heroku[web.1]: State changed from starting to up
2017-01-20T21:04:22.720627+00:00 heroku[router]: at=info method=GET path="/" host=sentieoapp1.herokuapp.com request_id=fb8bc13b-f6b5-47bc-8330-443f28e211df fwd="132.147.73.97" dyno=web.1 connect=0ms service=3ms status=200 bytes=627
2017-01-20T21:04:22.746761+00:00 app[web.1]: 10.158.165.5 - - [20/Jan/2017:21:04:22 +0000] "GET / HTTP/1.1" 200 386 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"
2017-01-20T21:04:23.076521+00:00 app[web.1]: 10.158.165.5 - - [20/Jan/2017:21:04:23 +0000] "GET /static/js/main.242e967b.js HTTP/1.1" 200 62263 "https://sentieoapp1.herokuapp.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"
2017-01-20T21:04:23.056416+00:00 heroku[router]: at=info method=GET path="/static/js/main.242e967b.js" host=sentieoapp1.herokuapp.com request_id=436d5ce5-ee39-4ab7-9e12-f5871e0fd552 fwd="132.147.73.97" dyno=web.1 connect=0ms service=25ms status=200 bytes=62540
2017-01-20T21:04:23.745285+00:00 heroku[router]: at=info method=GET path="/static/css/main.9a0fe4f1.css" host=sentieoapp1.herokuapp.com request_id=80438aaa-58c4-456e-8df9-7a29e49bc4ba fwd="132.147.73.97" dyno=web.1 connect=0ms service=2ms status=200 bytes=560
2017-01-20T21:04:23.766676+00:00 app[web.1]: 10.158.165.5 - - [20/Jan/2017:21:04:23 +0000] "GET /static/css/main.9a0fe4f1.css HTTP/1.1" 200 301 "https://sentieoapp1.herokuapp.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"
2017-01-20T21:04:24.044940+00:00 heroku[router]: at=info method=GET path="/static/media/logo.5d5d9eef.svg" host=sentieoapp1.herokuapp.com request_id=bcbc1906-3b90-4f13-a700-f432f79c725d fwd="132.147.73.97" dyno=web.1 connect=0ms service=1ms status=200 bytes=2902
2017-01-20T21:04:24.065013+00:00 app[web.1]: 10.158.165.5 - - [20/Jan/2017:21:04:24 +0000] "GET /static/media/logo.5d5d9eef.svg HTTP/1.1" 200 2671 "https://sentieoapp1.herokuapp.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"
2017-01-20T21:04:26.264631+00:00 heroku[router]: at=info method=GET path="/about" host=sentieoapp1.herokuapp.com request_id=0caef324-9268-4ebb-a3f5-0fb047100893 fwd="132.147.73.97" dyno=web.1 connect=0ms service=4ms status=404 bytes=403
2017-01-20T21:04:26.284717+00:00 app[web.1]: 10.158.165.5 - - [20/Jan/2017:21:04:26 +0000] "GET /about HTTP/1.1" 404 191 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"

这就是我卡住的地方。我对 Express 很熟悉,并且之前已经让它在 heroku 上工作,但这完全是另一个级别的噩梦。我知道这不是服务器端路由,而是从单个 index.html 页面中进行路由的反应。但是,如果我可以让它在我的本地机器上运行,为什么它不能在 Heroku 上运行?

【问题讨论】:

猜你的意思是客户端(动态)路由不适合你。当您访问 URL 时,Heroku 期望服务器端(静态)路由,因此您必须重新配置静态路由以首先加载“index.html”文件(默认情况下是 `\` 端点)。解决方案如下。 【参考方案1】:

实际上,我在搜索 react-router 和 heroku 文档 3 小时之前首先看到了这篇文章。对于 swyx 以及遇到相同问题的任何其他人,我将概述您需要做的最低限度的工作。

router.js - (显然将 AppSplash 和 AppDemo 更改为您的组件)

export default <Router history=hashHistory>
  <Route path="/" component=App>
    <IndexRoute component=AppSplash/>
    <Route path="demo" component=AppDemo/>
  </Route>
</Router>

app.js

import React,  Component  from 'react'

class App extends Component 
static propTypes = 
  children: PropTypes.node


render() 
  const  children  = this.props
  return (
    <div>
      children
    </div>
  )



export default App

在主目录的根目录中创建一个新文件并将其命名为 static.json。把这个放进去。


  "root": "build/",
  "clean_urls": false,
  "routes": 
    "/**": "index.html"
  

再次推送到heroku。这次路线应该可以工作。

说明:

您需要修改 Heroku 的默认 webpack,否则服务会混淆如何处理客户端路由。本质上是 static.json 的作用。其余的只是根据“react-router”文档处理路由的正确方法。

【讨论】:

哇,谢谢 HarshMarshmallow。这实际上可能使 create-react-app 对我有用,因为我可以将反应路由器放在它上面。自从我提出这个问题以来,我已经完全放弃了这条路线并寻找其他完全可部署的样板。无需测试即可标记您的正确 该解决方案是否适用于 GitHub 页面?我被封了两天...***.com/questions/49045867/… 对我来说这行不通,我在***.com/questions/52852877/…提出了一个新问题 仅供参考,这对我根本不起作用。添加static.json 的效果为零。 dhahn 的回答可以预见地有效,因为它确保 express 为与任何 express 路由不匹配的 react-router URL 提供索引文件。【参考方案2】:

如何修复客户端路由错误(Heroku 404 错误):

反应浏览器路由器

如果您使用 React Browser Router,作为带有 create-react-appnpm 模块,那么解决方案(对我有用)是创建一个static.json 文件(与package.json 在同一目录中)。


  "root": "build/",
  "clean_urls": false,
  "routes": 
    "/**": "index.html"
  

以下是此解决方案有效的原因:

Create-react-app 在很大程度上是一个 Node.Js 服务器,它服务于客户端 Reactpublic 静态目录映射到/ 端点,从浏览器访问此端点将下载index.html 网页。该网页依次加载 React 组件。并且因为 React Browser Router 是一个 React 组件,路由会在访问 / 端点后动态加载。换句话说,在加载 index.html 网页之前,我们所有的 React Browser Router 路由都会在 Heroku 上导致 404 错误。要解决此问题,可以使用static.json 文件将具有以下模式/** 的任何端点映射到index.html 文件,这反过来将加载React Browser Router 并正确加载该路由的react 组件。

来自 Apache HTTP 服务器:

同样,在 Apache HTTP 服务器上,在 public 目录中创建 .htaccess 文件,会将与 /** 匹配的所有端点重新映射到 index.html 文件。

Options -MultiViews
RewriteEngine On
RewriteCond %REQUEST_FILENAME !-f
RewriteRule ^ index.html [QSA,L]

更多资源

还请阅读create-react-app README 的“部署”部分,其中包含大量关于如何重新配置​​服务器以使用客户端路由的有用信息。

https://facebook.github.io/create-react-app/docs/deployment

反应静态路由器

最后,React Router 提供了一个静态路由器 React Static Router,它可以与 Node.js 上的“react-dom/server”npm 模块一起使用服务器,在服务器端渲染 JSX,不需要 static.json.htaccess 重新配置。

【讨论】:

这解决了我的问题,谢谢您的信息 @tfmontague 该解决方案是否适用于 GitHub 页面?我被封了两天...***.com/questions/49045867/… 对于 IIS/Windows/Azure 服务器,这个 tut 是一个很好的帮助。 medium.com/@to_pe/… @NullisTrue - facebook.github.io/create-react-app/docs/… - 可能有解决方案? @AnimeshSingh - facebook.github.io/create-react-app/docs/… - 还分享了几个用于自动部署到 Azure 的其他链接【参考方案3】:

试试这个:

app.get("*", (req, res) => 
  let url = path.join(__dirname, '../client/build', 'index.html');
  if (!url.startsWith('/app/')) // since we're on local windows
    url = url.substring(1);
  res.sendFile(url);
);

当我放入 server.js 时为我工作。

【讨论】:

这对我有用,但只有在确保我在某些情况下不返回 index.html 之后,例如在请求 transformed.js 时。我写了一个更详细的解释作为单独的帖子。谢谢!【参考方案4】:

Webpack+Express 解决方案

TL;DR

使用res.sendFile,但不要忘记同时返回transformed.js和其他静态文件。

说明

我一直在测试这个线程上的一些答案,但没有一个真正适用于以下设置:

我在本地机器上使用 webpack (-dev-server)。 在生产中(在 Heroku 上),我只是使用静态 express 服务器提供 webpack 构建输出。类似于app.use(express.static('$__dirname/build'));

这显然不适用于 react 路由器,因为 static 仅返回来自 build 文件夹(即 index.html)的实际文件,并且将在任何其他 url 上返回 404。

David Hahn 提出的使用res.sendFile 的解决方案为我指明了正确的方向。然而,劫持GET * 的主要问题是对transformed.js 的次要请求也会返回index.html。在修复代码以避免这种情况后,我能够得到一个可行的解决方案。

代码

这是我的server.js

const express = require("express");

const port = process.env.PORT || 8080;
var app = express();

// List of all the files that should be served as-is
let protected = ['transformed.js', 'main.css', 'favicon.ico']

app.get("*", (req, res) => 

  let path = req.params['0'].substring(1)

  if (protected.includes(path)) 
    // Return the actual file
    res.sendFile(`$__dirname/build/$path`);
   else 
    // Otherwise, redirect to /build/index.html
    res.sendFile(`$__dirname/build/index.html`);
  
);

app.listen(port, () => 
  console.log(`Server is up on port $port`);
);

很高兴讨论您的想法!我还不是 React 老手,所以可能有更好的方法。干杯!

【讨论】:

以上是关于React Routing 在本地机器上工作,但在 Heroku 上不工作的主要内容,如果未能解决你的问题,请参考以下文章

Django 在服务器上找不到静态文件,但在本地机器上工作正常

Tensorflow 训练模型在云端机器上工作,但在我的本地电脑上使用时出错

GWT rpc 在本地机器没有互联网连接的情况下无法工作,但在互联网上它可以工作

Heroku 当我在线部署我的应用程序时它崩溃但在我的本地机器上它可以正常工作

“没有这样的模块:AVFAudio”xcode 构建在旧机器上失败,但在新机器上工作正常

React 构建在本地运行,但在 Amplify 上失败