1. 概述
MERN 技术栈结合了 MongoDB、Express、React 和 Node.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' }
}));
✅ 使用 helmet
和 express-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 应用有所帮助。