1. 概述

MERN 技术栈结合了 MongoDBExpressReactNode.js,构建了一个全栈 JavaScript 开发环境,非常适合 Web 应用的开发。虽然开发过程令人兴奋,但正确部署应用才是确保其在生产环境中稳定、安全、高效运行的关键

本文将带你一步步了解如何正确部署一个 MERN 应用到托管服务上。

2. 部署前的准备工作

在部署之前,我们需要确保应用已经为生产环境做好准备。这个准备阶段主要包括以下三个步骤:

  • ✅ 优化 React 前端
  • ✅ 配置 Express 后端
  • ✅ 设置 MongoDB 连接

我们来逐个分析这些关键步骤。

2.1 优化 React 前端

首先,优化 React 前端的关键在于减少依赖和压缩打包体积。我们可以使用 Webpack 的生产模式来自动完成这些优化:

// webpack.config.js
module.exports = {
  mode: 'production',
  // 其他配置...
};

Webpack 在生产模式下会自动进行代码压缩和 Tree Shaking,从而减少打包体积、提升加载速度。

此外,我们还需要确保组件渲染高效。可以使用 React 的 useMemo 来避免不必要的重复计算:

import React, { useMemo } from 'react';

function ExpensiveComponent({ data }) {
  const processedData = useMemo(() => {
    // 耗时计算
    return data.map(item => item * 2);
  }, [data]);

  return <div>{processedData.join(', ')}</div>;
}

useMemo 可以缓存计算结果,避免重复渲染带来的性能损耗。

2.2 配置 Express 后端

前端优化完毕后,接下来是 Express 后端的配置。

生产环境下,我们需要设置合适的错误处理机制和启用压缩:

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

app.use(compression());

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('出错了!');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`服务运行在端口 ${PORT}`));

⚠️ 注意:PORT 使用了环境变量,确保部署时能适配托管平台分配的端口。

2.3 设置 MongoDB 连接

最后,确保 MongoDB 连接安全高效。我们可以使用环境变量保存敏感信息,并开启连接池:

const mongoose = require('mongoose');

const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost/myapp';

mongoose.connect(MONGODB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  poolSize: 10
});

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB 连接失败:'));
db.once('open', () => console.log('已连接到 MongoDB'));

✅ 通过环境变量传入连接字符串,便于托管平台动态配置。

3. 选择合适的托管服务

选择一个合适的托管平台,对应用的性能、可扩展性和维护性都有重要影响。

在选择托管服务时,需要考虑以下几个关键因素:

  • ✅ 是否支持 Node.js 和 MongoDB
  • ✅ 是否支持自动扩展
  • ✅ 部署流程是否简单,是否支持 CI/CD
  • ✅ 成本结构是否合理
  • ✅ 性能和可靠性是否达标

常见的 MERN 应用托管平台包括:

不同平台各有优势,应根据项目需求选择。

4. 部署前端

前端部署主要包括三个步骤:

  • ✅ 构建生产环境代码
  • ✅ 配置静态文件服务
  • ✅ 设置环境变量

我们来逐一说明。

4.1 构建 React 应用

执行以下命令生成生产环境代码:

npm run build

该命令会生成优化后的静态资源,放在 build 目录中。

4.2 配置静态文件服务

使用 Express 服务静态文件,可以这样配置:

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

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

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

✅ 上述代码实现了静态资源的托管,并支持 React Router 的客户端路由。

4.3 设置环境变量

前端环境变量通常以 REACT_APP_ 为前缀,例如:

heroku config:set REACT_APP_API_URL=https://my-api.herokuapp.com

在代码中通过 process.env 读取:

const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:3000/api';

fetch(`${API_URL}/users`)
  .then(response => response.json())
  .then(data => console.log(data));

⚠️ 注意:只有以 REACT_APP_ 开头的变量才会被暴露给前端代码,避免泄露敏感信息。

5. 部署后端

前端部署完成后,我们继续部署 Express 后端。

5.1 容器化 Express 服务

使用 Docker 容器化可以提升部署一致性:

FROM node:14

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

✅ 上述 Dockerfile 配置了一个 Node.js 容器环境。

5.2 配置服务器环境

后端部署还需要配置安全头和会话 cookie:

const express = require('express');
const helmet = require('helmet');
const session = require('express-session');
const app = express();

app.use(helmet());

app.use(express.json());

app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: { secure: true, httpOnly: true, sameSite: 'strict' }
}));

✅ 使用 helmetexpress-session 提升安全性。

5.3 处理数据库连接

生产环境下,MongoDB 连接需要支持重试和连接池:

const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      poolSize: 10
    });
    console.log('MongoDB 已连接');
  } catch (error) {
    console.error('MongoDB 连接失败:', error.message);
    process.exit(1);
  }
};

connectDB();

// 自动重连机制
mongoose.connection.on('disconnected', connectDB);

✅ 上述代码实现了连接池和断线自动重连机制。

6. 设置持续集成/持续部署(CI/CD)

为了提升部署效率,我们应配置 CI/CD 管道,实现自动化测试、构建和部署。

6.1 实现自动化测试

使用 Jest 编写单元测试:

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';

test('渲染 learn react 链接', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

✅ 自动化测试可以确保每次提交代码的质量。

6.2 配置部署管道

使用 GitHub Actions 实现 CI/CD 流程:

name: 部署

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: 使用 Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'
    - run: npm ci
    - run: npm test
    - run: npm run build
    - name: 部署到 Heroku
      uses: akhileshns/heroku-deploy@v3.12.12
      with:
        heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
        heroku_app_name: "your-app-name"
        heroku_email: "[email protected]"

✅ 上述配置实现了主分支提交后自动部署。

7. 保障部署应用的安全性

部署完成后,安全防护同样重要。主要包括:

  • ✅ 启用 HTTPS
  • ✅ 配置防火墙和访问控制
  • ✅ 实现身份验证和授权

7.1 启用 HTTPS

大多数托管平台默认支持 HTTPS。如果没有,可以使用 Let’s Encrypt 获取免费证书。

7.2 配置防火墙和访问控制

云平台通常提供安全组功能,用于控制入站和出站流量。裸金属或虚拟机则需手动配置防火墙规则。

7.3 实现身份验证和授权

使用 Passport.js 实现本地登录认证:

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) return done(err);
      if (!user) return done(null, false);
      if (!user.verifyPassword(password)) return done(null, false);
      return done(null, user);
    });
  }
));

app.post('/login', passport.authenticate('local', { 
  successRedirect: '/',
  failureRedirect: '/login' 
}));

✅ 上述代码实现了本地登录策略。

8. 监控与扩展

部署完成后,还需设置监控和扩展机制。

8.1 设置应用监控

可以使用 New Relic 或 Datadog 等工具进行应用性能监控。

8.2 实现日志和错误追踪

使用 Sentry 实现日志和错误追踪:

const Sentry = require("@sentry/node");

Sentry.init({ dsn: "SENTRY_DSN" });

app.use(Sentry.Handlers.requestHandler());

// 路由逻辑...

app.use(Sentry.Handlers.errorHandler());

✅ 上述代码集成了 Sentry 的错误处理中间件。

9. 总结

部署 MERN 应用是一个多阶段、多技术栈协作的过程,需要兼顾性能、安全、可扩展性等多个方面。

通过本文的指导,你可以:

  • ✅ 优化前后端代码
  • ✅ 选择合适的托管平台
  • ✅ 配置 CI/CD 流水线
  • ✅ 设置安全防护和监控机制

✅ 正确的部署流程可以显著提升应用的稳定性和可维护性。希望本文对你部署 MERN 应用有所帮助。


原始标题:Properly Deploy a MERN Application to a Hosting Service