אתה מריץ מספר אפליקציות React — קטלוג, סל קניות, חשבון אישי. כל אחת בונה את ה-bundle שלה, ואתה מגלה ש-React משוכפל, מה שמגדיל את גודל הטעינה. לאחרונה בפרויקט עם שמונה microfrontends, נתקלנו ב-bundle משתמש סופי של 2.4 MB. פיצול ה-bundle לא עוזר — אתה צריך runtime משותף. Module Federation — מנגנון המובנה בתוך Webpack Module Federation — פותר את זה. הניסיון שלנו: פרסנו ארכיטקטורה כזו עבור 30+ פרויקטים, והקטנו את גודל ה-bundle של כל remote ב-35% בממוצע, מה שהוביל לחיסכון משמעותי בתעבורת CDN.
באיזו גישת microfrontend לבחור: iframe, Web Components, או Module Federation?
Iframe פשוט אבל הורג SEO, שובר ניווט, ומאט: בפרויקט אחד, טעינת עמוד דרך iframe הוכפלה. Web Components מבודדים אבל מקשים על שיתוף state ו-context של React. Module Federation מספק קוד JS אמיתי, תלויות משותפות (bundle קטן פי 2), וטעינה דינמית ללא rebuild. השוואה בפרויקט אמיתי: iframe — 2.1 MB, Web Components — 1.8 MB, Module Federation — 680 KB. הוא תומך בטעינה אסינכרונית דרך תבנית bootstrap, קריטית כדי להימנע משגיאות Shared module is not available for eager consumption.
איך לתכנן ארכיטקטורת microfrontend ולהגדיר תלויות משותפות?
פיצלנו את המונולית ל-remotes לפי תחום עסקי.
| Remote | פונקציה | מאגר קוד |
|---|---|---|
| host (shell) | ניווט, layout, ניתוב | apps/shell |
| catalog | רשימת מוצרים ופרטים | apps/catalog |
| checkout | סל קניות ותשלום | apps/checkout |
| auth | התחברות, הרשמה, widget | apps/auth |
לכל remote יש pipeline CI/CD משלו. שינויים בקטלוג נראים מיד ללא פריסה מחדש של ה-host.
איך להימנע משכפול תלויות?
השתמש ב-singleton: true וב-requiredVersion אך ורק מ-package.json. אם הגרסאות לא תואמות, Module Federation טוען עותק משלו רק עבור מודולים לא תואמים. הניסיון שלנו מראה שגישה זו מפחיתה שגיאות אינטגרציה פי 3. בפרויקט אחד עם 8 remotes, הקטנו את ה-bundle המשתמש הסופי ב-48%, מה שהוריד משמעותית את עלויות התשתית.
השוואת גישות לשיתוף תלויות
| גישה | גודל bundle | מורכבות | גמישות גרסאות |
|---|---|---|---|
| Iframe | 2.1 MB | נמוכה | בידוד מלא |
| Web Components | 1.8 MB | גבוהה | מוגבלת |
| Module Federation | 680 KB | בינונית | אוטומטית |
איך להגדיר תלויות משותפות צעד אחר צעד?
- בקונפיגורציית ModuleFederationPlugin, ציין
sharedעם אובייקט עבור כל תלות. - הגדר
singleton: trueעבור ספריות קריטיות (React, React DOM). - הגדר
requiredVersionמ-package.json— לדוגמה,deps.react. - עבור ה-host, הגדר
eager: falseוהשתמש בתבנית bootstrap. - בדוק את האינטגרציה, וודא שאין שגיאות console.
איך להגדיר Webpack עבור host ו-remote?
קונפיגורציית host (shell)
const { ModuleFederationPlugin } = require('webpack').container
const HtmlWebpackPlugin = require('html-webpack-plugin')
const deps = require('./package.json').dependencies
module.exports = (env, argv) => ({
mode: argv.mode ?? 'development',
entry: './src/index.ts',
output: {
publicPath: 'auto',
filename: '[name].[contenthash].js',
clean: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react', '@babel/preset-typescript'],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
catalog: `catalog@${ argv.mode === 'production' ? 'https://catalog.example.com' : 'http://localhost:3001' }/remoteEntry.js`,
checkout: `checkout@${ argv.mode === 'production' ? 'https://checkout.example.com' : 'http://localhost:3002' }/remoteEntry.js`,
auth: `auth@${ argv.mode === 'production' ? 'https://auth.example.com' : 'http://localhost:3003' }/remoteEntry.js`,
},
shared: {
react: {
singleton: true,
requiredVersion: deps.react,
eager: false,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
eager: false,
},
'react-router-dom': {
singleton: true,
requiredVersion: deps['react-router-dom'],
},
},
}),
new HtmlWebpackPlugin({
template: './public/index.html'
}),
],
devServer: {
port: 3000,
historyApiFallback: true,
},
}) קונפיגורציית remote (catalog)
const { ModuleFederationPlugin } = require('webpack').container
const deps = require('./package.json').dependencies
module.exports = (env, argv) => ({
mode: argv.mode ?? 'development',
entry: './src/index.ts',
output: {
publicPath: 'auto',
filename: '[name].[contenthash].js',
clean: true,
},
plugins: [
new ModuleFederationPlugin({
name: 'catalog',
filename: 'remoteEntry.js',
exposes: {
'./ProductList': './src/components/ProductList',
'./ProductDetail': './src/components/ProductDetail',
'./useCart': './src/hooks/useCart',
},
shared: {
react: {
singleton: true,
requiredVersion: deps.react,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
},
'react-router-dom': {
singleton: true,
},
},
}),
],
devServer: {
port: 3001,
headers: {
'Access-Control-Allow-Origin': '*'
},
historyApiFallback: true,
},
}) איך להבטיח טעינה אסינכרונית? תבנית Bootstrap
// src/index.ts
import('./bootstrap')
// src/bootstrap.tsx
import React from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
const root = createRoot(document.getElementById('root')!)
root.render(<App />)בלי זה, אתה מקבל const { ModuleFederationPlugin } = require('webpack').container const HtmlWebpackPlugin = require('html-webpack-plugin') const deps = require('./package.json').dependencies module.exports = (env, argv) => ({ mode: argv.mode ?? 'development', entry: './src/index.ts', output: { publicPath: 'auto', filename: '[name].[contenthash].js', clean: true, }, resolve: { extensions: ['.ts', '.tsx', '.js'], }, module: { rules: [ { test: /\.(ts|tsx)$/, loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-typescript'], }, }, ], }, plugins: [ new ModuleFederationPlugin({ name: 'shell', remotes: { catalog: `catalog@${ argv.mode === 'production' ? 'https://catalog.example.com' : 'http://localhost:3001' }/remoteEntry.js`, checkout: `checkout@${ argv.mode === 'production' ? 'https://checkout.example.com' : 'http://localhost:3002' }/remoteEntry.js`, auth: `auth@${ argv.mode === 'production' ? 'https://auth.example.com' : 'http://localhost:3003' }/remoteEntry.js`, }, shared: { react: { singleton: true, requiredVersion: deps.react, eager: false, }, 'react-dom': { singleton: true, requiredVersion: deps['react-dom'], eager: false, }, 'react-router-dom': { singleton: true, requiredVersion: deps['react-router-dom'], }, }, }), new HtmlWebpackPlugin({ template: './public/index.html' }), ], devServer: { port: 3000, historyApiFallback: true, }, }) . אנו מבטיחים שתבנית זו מיושמת בכל remote.
הצהרות TypeScript עבור מודולי remote ו-CI/CD
npm install @module-federation/typescript הוסף const { ModuleFederationPlugin } = require('webpack').container const deps = require('./package.json').dependencies module.exports = (env, argv) => ({ mode: argv.mode ?? 'development', entry: './src/index.ts', output: { publicPath: 'auto', filename: '[name].[contenthash].js', clean: true, }, plugins: [ new ModuleFederationPlugin({ name: 'catalog', filename: 'remoteEntry.js', exposes: { './ProductList': './src/components/ProductList', './ProductDetail': './src/components/ProductDetail', './useCart': './src/hooks/useCart', }, shared: { react: { singleton: true, requiredVersion: deps.react, }, 'react-dom': { singleton: true, requiredVersion: deps['react-dom'], }, 'react-router-dom': { singleton: true, }, }, }), ], devServer: { port: 3001, headers: { 'Access-Control-Allow-Origin': '*' }, historyApiFallback: true, }, }) לקונפיגורציות של host ו-remote — הטיפוסים נוצרים אוטומטית כ-// src/index.ts import('./bootstrap') // src/bootstrap.tsx import React from 'react' import { createRoot } from 'react-dom/client' import App from './App' const root = createRoot(document.getElementById('root')!) root.render(<App />) . אין יותר Shared module is not available for eager consumption.
CI/CD לפריסה עצמאית
---
name: Deploy Catalog
on:
push:
branches: [main]
paths: ['apps/catalog/**']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd apps/catalog && npm ci && npm run build
- name: Deploy to CDN
run: aws s3 sync apps/catalog/dist s3://catalog.example.com --delete
- name: Invalidate CloudFront
run: aws cloudfront create-invalidation --distribution-id $CF_ID --paths "/*"
---ה-host מקבל את ה-remote המעודכן ללא פריסה משלו — בטעינת העמוד הבאה.
מה כלול בעבודה
- ביקורת על הארכיטקטורה הנוכחית וזיהוי נקודות צמיחה
- תכנון סכמת remotes ותלויות משותפות
- קונפיגורציית Webpack עבור host וכל ה-remotes תוך התחשבות בסביבת production
- אינטגרציית תבנית bootstrap למניעת שגיאות צריכה מוקדמת
- הגדרת CI/CD עבור כל remote (GitHub Actions או Jenkins)
- יצירת טיפוסי TypeScript דרך FederatedTypesPlugin
- תיעוד לפריסה ותמיכה
- הכשרת צוות בנושא microfrontends
אילו תוצאות אפשר לצפות?
לאחר יישום Module Federation, אתה מקבל: הפחתה של 30-50% בזמן הטעינה, מחזורי פריסה עצמאיים לכל remote, ופחות קונפליקטים בפיתוח. בפרויקט אחד, הקטנו את גודל ה-JS הממוצע מ-1.2 MB ל-680 KB. חיסכון בעלויות תעבורה יכול להיות משמעותי עם תעבורה גבוהה.
טעויות קונפיגורציה נפוצות
- חוסר
npm install @module-federation/typescriptעבור React — שכפול תלויות. - חוסר
FederatedTypesPluginעל תלויות משותפות — שגיאת אתחול. - קונפיגורציית CORS שגויה עבור remoteEntry.
כדי ש-remoteEntry יעבוד בין דומיינים שונים, הוסף כותרות @mf-types/catalog/ProductList בשרת המארח את ה-remote. ב-Webpack devServer, זה נעשה דרך אפשרות any. ב-production, דרך שרת האינטרנט או הגדרות CDN. זה חובה.
הזמן יישום — נגדיר את הפרויקט שלך תוך 5–10 ימי עבודה. קבל ייעוץ — צור קשר לביקורת ארכיטקטורה חינמית.







