如何构建由Node.js(Express)处理URL路由的MERN Stack应用?
整合Express与React并由Node负责路由的实现方案
一、基础项目搭建
先初始化项目并安装依赖:
mkdir react-express-integrated && cd react-express-integrated npm init -y # 核心依赖 npm install express react react-dom # 开发依赖 npm install --save-dev webpack webpack-cli webpack-dev-middleware webpack-hot-middleware babel-loader @babel/core @babel/preset-react html-webpack-plugin
推荐项目结构:
react-express-integrated/ ├── src/ │ ├── client/ │ │ ├── components/ │ │ │ ├── Home.jsx │ │ │ └── About.jsx │ │ ├── App.jsx │ │ ├── index.jsx │ │ └── index.html │ └── server/ │ └── server.js ├── webpack.config.js └── package.json
二、Express路由核心配置
在src/server/server.js中,先定义API路由,再将所有非API请求导向React入口页面,实现Node统一路由:
const express = require('express'); const path = require('path'); const app = express(); // 1. 处理API路由(需放在静态文件和兜底路由之前) app.get('/api/user', (req, res) => { res.json({ name: 'Marc', role: 'Developer' }); }); // 2. 生产环境:提供React打包后的静态文件 if (process.env.NODE_ENV === 'production') { app.use(express.static(path.join(__dirname, '../../dist'))); } // 3. 开发环境:启用Webpack热重载 if (process.env.NODE_ENV !== 'production') { const webpack = require('webpack'); const webpackConfig = require('../../webpack.config')({ production: false }); const compiler = webpack(webpackConfig); app.use(require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath })); app.use(require('webpack-hot-middleware')(compiler)); } // 4. 兜底路由:所有非API请求返回React入口页面 app.get('*', (req, res) => { // 开发环境从Webpack内存读取,生产环境从dist文件夹读取 const htmlPath = process.env.NODE_ENV === 'production' ? path.join(__dirname, '../../dist/index.html') : path.join(__dirname, '../../src/client/index.html'); res.sendFile(htmlPath); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });
三、React与Webpack配置
- React入口模板
src/client/index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>React + Express App</title> </head> <body> <div id="root"></div> </body> </html>
- React入口文件
src/client/index.jsx:
import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import App from './App'; // 必须使用BrowserRouter,让Node处理路由映射 const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <BrowserRouter> <App /> </BrowserRouter> );
- React路由示例
src/client/App.jsx:
import { Routes, Route, Link } from 'react-router-dom'; import Home from './components/Home'; import About from './components/About'; function App() { return ( <div style={{ padding: '20px' }}> <nav> <Link to="/" style={{ marginRight: '15px' }}>首页</Link> <Link to="/about">关于</Link> </nav> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </div> ); } export default App;
- Webpack配置文件
webpack.config.js:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); module.exports = (env) => { const isProduction = env.production; return { entry: isProduction ? './src/client/index.jsx' : ['webpack-hot-middleware/client?reload=true', './src/client/index.jsx'], output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.[contenthash].js', publicPath: '/', clean: true // 生产环境打包时清空dist文件夹 }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react'] } } } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/client/index.html', minify: isProduction // 生产环境压缩HTML }), ...(isProduction ? [] : [new webpack.HotModuleReplacementPlugin()]) ], resolve: { extensions: ['.js', '.jsx'] }, mode: isProduction ? 'production' : 'development', devtool: isProduction ? 'source-map' : 'inline-source-map' }; };
四、运行脚本配置
在package.json中添加以下脚本:
"scripts": { "dev": "NODE_ENV=development node src/server/server.js", "build": "webpack --env production", "start": "NODE_ENV=production node src/server/server.js" }
- 开发环境:执行
npm run dev,访问http://localhost:3000即可看到React页面,支持热重载 - 生产环境:先执行
npm run build打包React代码到dist文件夹,再执行npm start启动生产服务器
五、关键注意事项
- 必须使用
BrowserRouter而非HashRouter,否则React路由会绕过Node的路由处理 - API路由必须放在兜底路由之前,否则API请求会被React页面拦截
- 开发环境下Webpack-dev-middleware将打包文件存在内存中,不会写入磁盘,提升开发效率
内容的提问来源于stack exchange,提问作者Marc
相关产品推荐
相关产品推荐

