如何构建分属不同端口、多功能区的React前端应用架构?
Great question! Since you’re transitioning from a single create-react-app (CRA) setup to three separate port-specific UIs (admin, client, guest) while needing shared UI components, here are the most practical architecture approaches tailored to your stack:
1. Monorepo with Separate CRA Apps + Shared Component Package
This is the most balanced approach for teams wanting both app independence and easy component sharing. You’ll use a monorepo tool like Yarn Workspaces or Nx to house three distinct CRA projects and a shared component library.
How to set it up:
- Structure your project like this:
your-root-project/ ├── packages/ │ ├── admin-app/ # CRA for localhost:9001 │ │ ├── src/ │ │ └── package.json (start script: "react-scripts start --port 9001") │ ├── client-app/ # CRA for localhost:9002 │ │ ├── src/ │ │ └── package.json (start script: "react-scripts start --port 9002") │ ├── guest-app/ # CRA for localhost:9003 │ │ ├── src/ │ │ └── package.json (start script: "react-scripts start --port 9003") │ └── shared-components/ # Reusable UI library │ ├── src/ │ │ ├── Button.jsx │ │ ├── Layout.jsx │ │ └── ... │ └── package.json └── package.json (root config enabling workspaces) - Register the shared package in each app’s
package.json(e.g.,@your-org/shared-components) and import components like:import { Button, Layout } from '@your-org/shared-components';
Pros:
- Each app maintains full autonomy for its unique UI features (e.g., admin-specific dashboards, guest-only landing pages)
- Shared components are version-controlled in one place, ensuring consistency across all UIs
- Easy to scale: add new apps or update shared components without disrupting existing code
- CRA works seamlessly in monorepos with minimal configuration
Cons:
- Requires initial setup of monorepo tooling (minor learning curve if you’re new to it)
2. Single CRA App with Dynamic Entry Points + Multi-Port Serving
If you prefer avoiding a monorepo, you can modify a single CRA to serve three distinct UIs via different ports, using dynamic entry points. You’ll need to override CRA’s default Webpack config (use react-app-rewired instead of ejecting to keep CRA updates manageable).
How to set it up:
- Organize your
srcfolder to split app-specific code:src/ ├── admin/ │ ├── index.jsx (admin app entry) │ └── AdminDashboard.jsx ├── client/ │ ├── index.jsx (client app entry) │ └── UserProfile.jsx ├── guest/ │ ├── index.jsx (guest app entry) │ └── LandingPage.jsx └── shared/ ├── Button.jsx └── Layout.jsx - Use
react-app-rewiredto update Webpack’s entry points inconfig-overrides.js:module.exports = function override(config) { config.entry = { admin: './src/admin/index.jsx', client: './src/client/index.jsx', guest: './src/guest/index.jsx', }; config.output.filename = 'static/js/[name].bundle.js'; // Unique bundle per app return config; }; - Use
concurrentlyto start all three ports at once:"scripts": { "start:admin": "PORT=9001 react-app-rewired start", "start:client": "PORT=9002 react-app-rewired start", "start:guest": "PORT=9003 react-app-rewired start", "start": "concurrently \"npm run start:admin\" \"npm run start:client\" \"npm run start:guest\"" }
Pros:
- No monorepo complexity; all code lives in a single project
- Shared components are directly accessible without cross-package imports
- Faster initial setup if you’re already familiar with CRA
Cons:
- Shared dependencies mean you can’t have app-specific package versions (minor issue if all apps use the same React version)
- Webpack overrides may break with future CRA updates (though
react-app-rewiredusually keeps up) - Less separation between apps, which can lead to accidental code leaks if not careful
3. Micro-Frontends with Webpack Module Federation
If you anticipate the three apps being maintained by separate teams or needing independent deployment down the line, micro-frontends are a scalable long-term solution. Using Webpack 5’s Module Federation, each CRA app acts as a standalone "micro-app" that can import shared components (or even entire features) from a remote shared library.
How to set it up:
- Create four separate CRA projects: one for each UI (admin, client, guest) and one for shared components
- Configure Module Federation in the shared component library to expose its components as a remote module
- Update each UI app to import components from the shared remote module
Pros:
- Full independence for each app (deploy updates to admin without touching client/guest)
- Teams can work on separate apps without conflicting codebase changes
- Flexible: you could even switch one app to a different framework later (though you’re using React now)
Cons:
- Significantly more complex configuration (Module Federation has a steep learning curve)
- Runtime dependency management can be tricky (e.g., ensuring shared React versions across apps)
- Debugging cross-app issues is harder due to the distributed nature
My Top Recommendation
For most teams in your scenario, Option 1 (Monorepo with separate CRA apps) is the sweet spot. It balances the need for app-specific customization with easy shared component maintenance, and it’s straightforward to implement with tools like Yarn Workspaces. If you want to avoid monorepo overhead, Option 2 works well for smaller teams or simpler projects. Option 3 is best reserved for larger teams with long-term scaling needs.
Don’t forget to align each app’s API calls with your Hapi backend’s auth mechanisms: the admin app should send admin-specific tokens, the client app uses user tokens, and the guest app either uses no auth or limited guest tokens.
内容的提问来源于stack exchange,提问作者Stoyan Berov

