如何使用后端文件夹内的前端将 MERN 应用程序部署到 Heroku

Posted

技术标签:

【中文标题】如何使用后端文件夹内的前端将 MERN 应用程序部署到 Heroku【英文标题】:How to deploy MERN app to Heroku with frontend inside of backend folder 【发布时间】:2021-11-12 14:39:12 【问题描述】:

将我的 MERN 应用程序部署到 heroku 表示构建已成功,但在打开应用程序时,它显示的只是未找到。我怀疑路由和路径存在问题,尤其是在后端的 index.js 中。如标题所述,前端位于后端文件夹内,因此两者只能使用一个网站。我已遵循 this GitHub repo 的所有建议来执行此过程。我在包含代码方面做得太过火了,因为我不知道问题出在哪里,而且正如您在生产中所知道的那样,这一切都是相互关联的。据我了解,在 heroku-postbuild 之前,我不需要运行 React 的构建/生产文件夹,但 heroku 找不到构建文件夹或其 index.html

项目结构

api //frontend is inside backend for the benefit of only deploying to one site
-mappinapp //the react frontend
----node_modules
----public
------index.html
----src
------components
------App.js
------config.js
------index.js
----.env
----.gitignore
----package-lock.json
----package.json
-models
-node_modules
-routes
-.env
-.gitignore
-index.js
-package-lock.json
-package.json

Heroku 日志 --tail

heroku[router]: at=info method=GET path="/" host=mappinapp.herokuapp.com request_id=redacted fwd="redacted" dyno=web.1 connect=0ms service=9ms status=404 bytes=380 protocol=httpsapp[web.1]: Error: ENOENT: no such file or directory, stat '/app/mappinapp/build/index.html'

api(后端) package.json


  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": 
    "start": "node index.js",
    "heroku-postbuild":  "cd mappinapp && npm install && npm run build" //build succeeded on heroku
  ,
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": 
    "bcrypt": "^5.0.1",
    "dotenv": "^8.6.0",
    "express": "^4.17.1",
    "mongoose": "^5.13.9",
    "nodemon": "^2.0.7",
    "path": "^0.12.7"
  

api(后端)的index.js

const express = require("express");
const app = express();
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const userRoute = require("./routes/users");
const pinRoute = require("./routes/pins");
const path = require("path");

dotenv.config();

app.use(express.json());

mongoose 
 .connect(process.env.MONGO_URL, 
        useNewUrlParser: true,
        useUnifiedTopology: true,
        useCreateIndex: true,   )   
 .then(() => console.log("MongoDB connected!"))
 .catch(err => console.log(err));

app.use("/api/users", userRoute);
app.use("/api/pins", pinRoute);

// app.use(express.static(path.join(__dirname, "/api/mappinapp/build")));

app.get('*', function (req, res) 
  const index = path.join(__dirname, '/mappinapp/build', 'index.html');
  console.log(__dirname);
  res.sendFile(index);
);

app.listen(process.env.PORT || 5000, () => 
  console.log("Backend server is running!");
);

在阅读this post后注释掉了app.use行并添加了app.get函数 我确实注意到将鼠标悬停在“/api/mappinapp/build”或“/mappinapp/build”上并不会像“./routes/users”那样显示路径。我已经尝试以多种方式更改路径并使用 app.use(express... 行。我怀疑这是问题所在,但可能不是。

config.js

import axios from "axios";

export const axiosInstance = axios.create(
    baseURL: "https://mappinapp.herokuapp.com/api/"
)

api(后端) .gitignore

/node_modules
.env

前端.gitignore

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production - removed /build in case having it in the .gitignore would cause issues.

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

在 heroku 上打开应用显示 NOT FOUND,控制台显示

Content Security Policy: The page’s settings blocked the loading of a resource at inline (“default-src”). moz-extension:1592:49
Content Security Policy: The page’s settings blocked the loading of a resource at https://mappinapp.herokuapp.com/favicon.ico (“default-src”). resource:191:19
Content Security Policy: The page’s settings blocked the loading of a resource at inline (“default-src”). moz-extension:199:11

不确定是否相关,但我包括在内

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self';" />

在前端 react 应用的 index.html 中。在浏览器控制台日志中仍然显示相同的错误。

最后,

heroku 构建日志

remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Building on the Heroku-20 stack
remote: -----> Using buildpack: heroku/nodejs
remote: -----> Node.js app detected
remote:        
remote: -----> Creating runtime environment
remote:        
remote:        NPM_CONFIG_LOGLEVEL=error
remote:        NODE_VERBOSE=false
remote:        NODE_ENV=production
remote:        NODE_MODULES_CACHE=true
remote:        
remote: -----> Installing binaries
remote:        engines.node (package.json):  unspecified
remote:        engines.npm (package.json):   unspecified (use default)
remote:        
remote:        Resolving node version 14.x...
remote:        Downloading and installing node 14.17.6...
remote:        Using default npm version: 6.14.15
remote:        
remote: -----> Restoring cache
remote:        - node_modules
remote:        
remote: -----> Installing dependencies
remote:        Installing node modules
remote:        
remote:        > bcrypt@5.0.1 install /tmp/build_4d6aebd0/node_modules/bcrypt
remote:        > node-pre-gyp install --fallback-to-build
remote:        
remote:        [bcrypt] Success: "/tmp/build_4d6aebd0/node_modules/bcrypt/lib/binding/napi-v3/bcrypt_lib.node" is installed via remote
remote:        
remote:        > nodemon@2.0.12 postinstall /tmp/build_4d6aebd0/node_modules/nodemon
remote:        > node bin/postinstall || exit 0
remote:        
remote:        Love nodemon? You can now support the project via the open collective:
remote:         > https://opencollective.com/nodemon/donate
remote:        
remote:        added 248 packages in 5.263s
remote:        
remote: -----> Build
remote:        Running heroku-postbuild
remote:        
remote:        > api@1.0.0 heroku-postbuild /tmp/build_4d6aebd0
remote:        > cd mappinapp && npm install && npm run build
remote:        
remote:        audited 248 packages in 2.297s
remote:        
remote:        15 packages are looking for funding
remote:          run `npm fund` for details
remote:        
remote:        found 0 vulnerabilities
remote:        
remote:        
remote: -----> Caching build
remote:        - node_modules
remote:        
remote: -----> Pruning devDependencies
remote:        audited 248 packages in 2.168s
remote:        
remote:        15 packages are looking for funding
remote:          run `npm fund` for details
remote:        
remote:        found 0 vulnerabilities
remote:        
remote:        
remote: -----> Build succeeded!
remote: -----> Discovering process types
remote:        Procfile declares types     -> (none)
remote:        Default types for buildpack -> web
remote: 
remote: -----> Compressing...
remote:        Done: 35.8M
remote: -----> Launching...
remote:        Released v16
remote:        https://mappinapp.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.

【问题讨论】:

【参考方案1】:

就在server.js中的app.listen()上方,我通常在部署Heroku时添加以下内容:

if (process.env.NODE_ENV === 'production') 
    app.use(express.static(path.join(__dirname, 'mappinapp', 'build')));

    app.get('*', (req, res) => 
        res.sendFile(path.resolve(__dirname, 'mappinapp', 'build', 'index.html'));
    );


在根目录中创建Procfile

web: npm install
web: npm start

并确保此脚本位于package.json:

"scripts": 
        "start": "node server",
        "server": "nodemon server",
        "client": "npm run start --prefix mappinapp",
        "heroku-postbuild": "npm install --prefix client && npm run build --prefix client"

【讨论】:

没有任何理由将npm install 作为web 进程运行。 javascript buildpack 应该在部署过程中安装您的依赖项。

以上是关于如何使用后端文件夹内的前端将 MERN 应用程序部署到 Heroku的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 axios 进行 OAuth 登录

在 MERN 堆栈 Web 应用程序中存储图像的最佳方式

如何使用 MERN 提供静态 html 文件

如何在 MERN 堆栈中向用户显示错误

Mern,删除评论问题

两个独立的 Heroku(Node 后端和 React 前端)之间的通信?