如何将静态 React "app" 部署到 Heroku?

How do I deploy a static React "app" to Heroku?

标题是:如何在不导致 "Must use import to load ES Module" 错误的情况下修复 Heroku 上的 "Cannot use import statement outside a module" 错误?

这是在我了解到我正在尝试部署 "static" React "app" 并且 Heroku 运行 index.js 就好像它是服务器代码之前。

我已设置 Procfile 以包含:

web: node ./src/index.js

然后我看到这个错误:

...
2020-03-29T02:34:01.000000+00:00 app[api]: Build succeeded
2020-03-29T02:34:02.637867+00:00 heroku[web.1]: State changed from starting to crashed
2020-03-29T02:34:02.622526+00:00 heroku[web.1]: Process exited with status 1
2020-03-29T02:34:02.583667+00:00 app[web.1]: /app/src/index.js:1
2020-03-29T02:34:02.583689+00:00 app[web.1]: import React from 'react';
2020-03-29T02:34:02.583690+00:00 app[web.1]: ^^^^^^
2020-03-29T02:34:02.583690+00:00 app[web.1]:
2020-03-29T02:34:02.583691+00:00 app[web.1]: SyntaxError: Cannot use import statement outside a module
2020-03-29T02:34:02.583691+00:00 app[web.1]: at wrapSafe (internal/modules/cjs/loader.js:1072:16)
...

我试图通过将此添加到 package.json 来修复它:

  "type": "module",

然后我得到这个错误:

...
2020-03-29T03:05:01.000000+00:00 app[api]: Build succeeded
2020-03-29T03:05:06.293573+00:00 heroku[web.1]: Starting process with command `node ./src/index.js`
2020-03-29T03:05:08.744086+00:00 heroku[web.1]: State changed from starting to crashed
2020-03-29T03:05:08.724957+00:00 heroku[web.1]: Process exited with status 1
2020-03-29T03:05:08.650103+00:00 app[web.1]: internal/modules/cjs/loader.js:1174
2020-03-29T03:05:08.650125+00:00 app[web.1]: throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
2020-03-29T03:05:08.650126+00:00 app[web.1]: ^
2020-03-29T03:05:08.650126+00:00 app[web.1]:
2020-03-29T03:05:08.650126+00:00 app[web.1]: Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /app/src/index.js
2020-03-29T03:05:08.650127+00:00 app[web.1]: at Object.Module._extensions..js (internal/modules/cjs/loader.js:1174:13)
2020-03-29T03:05:08.650127+00:00 app[web.1]: at Module.load (internal/modules/cjs/loader.js:1002:32)
...

我应该注意到我的 React "app" 是一个 front-end 仅使用我已经部署在其他地方的 GraphQL API。也就是说,没有"server.js that serves [my] react app"。 (HMR 的评论让我意识到了这一点。我想正确的术语应该是称其为 "static app"。)

server.js 中的这段代码仅根据 Tin Nguyen 的答案稍作改编:

const http = require('http');
const path = require('path');
const express = require('express');

let wss;
let server;
const app = express();
app.use(express.static(path.join(__dirname, './build')));

server = new http.createServer(app);

server.on('error', err => console.log('Server error:', err));
server.listen(process.env.PORT);

...用这个 Procfile:

web: node ./server.js

唯一的变化是构建目录的路径,我在使用 heroku run bash 窥探后对其进行了调整。

我的网站运行不正常,但我认为这是我构建不正确的结果。也许这可以通过构建包来解决。但这属于另一个问题。 (而且我可能懒得弄清楚,因为我打算听取 Tin Nguyen 的建议并尝试在 GitHub 上托管。)

更新:问题不在于我的构建,而在于 client-side 路由(如 https://create-react-app.dev/docs/deployment/ 中所述)。此代码(来自该页面)修复了它(在 server.js 中):

const express = require('express');
const path = require('path');
const app = express();

app.use(express.static(path.join(__dirname, 'build')));

app.get('/*', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(process.env.PORT);

link in HMR's comment 帮助我达到了这个理解,但 Dave Ceddia 写道,他的文章涵盖了如何将 React 应用程序和 API 服务器保持在一起。是否有关于如何在 Heroku(或其他地方)上部署 front-end 且已在其他地方部署 API 服务器的文档?

(我找到了 where Paras recommends Netlify。)

实际上,这来自于在 NodeJS 环境中使用 ES6 import。有两种方法,在服务器端 JavaScript 文件中使用 require 而不是 import

import something from 'something';

↓↓↓

const something = require('something');

在服务器环境中使用babel node到运行项目:

node ./src/index.js

↓↓↓

babel-node [options] [ -e script | ./src/index.js ] [arguments]

一个完整的例子如下:

npx babel-node --presets es2015 -- ./src/index.js

通过阅读您的错误,我想第二种方法更适合您的情况。但首先请删除您的解决方案,我的意思是 "type": "module",.

尝试更改您的命令:

node ./src/index.js

至:

node --experimental-modules ./src/index.js

尝试使用

修复package.json和不修复package.json
"type": "module"

如果我没看错的话,你有一个静态网站,你希望它托管在 Heroku 上。

静态网站可以在 Netlify 和 GitHub 页面上提供服务。他们不需要网络服务器。您仍然可以通过围绕它包装一个 Web 服务器来将其托管在 Heroku 上,然后为您的静态文件提供服务。

const http = require('http');
const path = require('path');
const express = require('express');

let wss;
let server;
const app = express();
app.use(express.static(path.join(__dirname, './../build')));

server = new http.createServer(app);

server.on('error', err => console.log('Server error:', err));
server.listen(process.env.PORT);

你怎么知道你有一个静态网站?
您应该已经在项目的某处生成了构建文件。您可以导航到该文件夹​​并打开 index.html。即使您没有 nodenpm 运行,该网站也应该可以运行。该文件夹在我的示例中 ./../build.

我个人建议您通过 Heroku 在 GitHub 上托管静态网站。 Heroku 是 "free" 但有很多限制。 dyno 时间有限,网站需要在长时间未使用后启动等