如何构建单package.json与node_modules的React+Express前后端项目
package.json Great question! This streamlined setup with a single package.json and shared node_modules is perfect for keeping your project organized without redundant configs. Here's my battle-tested implementation:
Step 1: Initialize Project Structure
First, set up your base folders and root package.json:
mkdir react-express-monorepo cd react-express-monorepo npm init -y mkdir -p client/{public,src} server
Step 2: Install All Dependencies
We'll bundle both React (via react-scripts) and Express dependencies in the root package.json:
# Core dependencies npm install express react react-dom react-scripts cors # Dev tools (for concurrent dev servers and backend hot-reload) npm install --save-dev concurrently nodemon react-app-rewired
Step 3: Set Up React Frontend
Since we're skipping the standalone create-react-app package.json, we'll manually add the core React files:
- client/public/index.html (copy this standard CRA template):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="React + Express Monorepo" /> <title>React + Express App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> </body> </html>
- client/src/index.js:
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );
- client/src/App.js:
import React, { useEffect, useState } from 'react'; import './App.css'; function App() { const [message, setMessage] = useState(''); useEffect(() => { // Fetch data from Express backend fetch('http://localhost:5000/api/hello') .then(res => res.json()) .then(data => setMessage(data.message)); }, []); return ( <div className="App"> <header className="App-header"> <h1>React + Express Monorepo</h1> {message && <p>{message}</p>} </header> </div> ); } export default App;
- Add CSS files: Copy the default
index.cssandApp.cssfrom a standard CRA setup intoclient/src/.
Step 4: Configure React Build Output
By default, react-scripts builds to a root build folder. We'll use react-app-rewired to redirect builds to client/build without ejecting:
Create a root config-overrides.js file:
const path = require('path'); module.exports = { paths: (paths) => { paths.appBuild = path.resolve(__dirname, 'client/build'); return paths; }, };
Step 5: Set Up Express Backend
Create server/server.js with this basic setup (includes static file serving for production and API routes):
const express = require('express'); const path = require('path'); const cors = require('cors'); const app = express(); const PORT = process.env.PORT || 5000; // Enable CORS for dev environment app.use(cors()); // Parse JSON requests app.use(express.json()); // Example API route app.get('/api/hello', (req, res) => { res.json({ message: 'Hello from your Express backend!' }); }); // Serve React static files in production if (process.env.NODE_ENV === 'production') { app.use(express.static(path.join(__dirname, '../client/build'))); // Catch-all route to serve React app app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../client/build/index.html')); }); } app.listen(PORT, () => { console.log(`Server running on port ${PORT} 🚀`); });
Step 6: Configure Root package.json Scripts
Update your root package.json scripts to handle development, production, and builds seamlessly:
"scripts": { "start": "NODE_ENV=production node server/server.js", "dev": "concurrently \"npm run server\" \"npm run client\"", "server": "nodemon server/server.js", "client": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-scripts eject" }
Script Breakdown:
npm run dev: Starts both the Express backend (with nodemon hot-reload) and React dev server simultaneously.npm run server: Runs only the Express backend with hot-reload.npm run client: Runs only the React dev server.npm run build: Builds the React app toclient/build.npm start: Runs the production server (serves the built React app + API).
Step 7: Set Up .gitignore
Create a root .gitignore file to exclude unnecessary files:
node_modules/ client/build/ .env npm-debug.log* yarn-debug.log* yarn-error.log* .DS_Store .vscode/ .idea/
Key Tips for Maintenance
- Dependency Management: All packages are installed at the root, so
npm installhandles everything—no need to run installs in subfolders. - Environment Variables: Use a root
.envfile for backend variables, and prefix React variables withREACT_APP_(react-scripts will pick them up automatically). - Testing: Keep test files alongside your components/backend code (e.g.,
client/src/App.test.js,server/server.test.js)—react-app-rewired testwill handle React tests, and you can add a separate test script for backend with Jest if needed.
内容的提问来源于stack exchange,提问作者user8679481

