در چشمانداز در حال تحول توسعه فرانتاند، برنامههای مونولیتیک اغلب به گلوگاه برای تیمهای مهندسی بزرگ تبدیل میشوند. با رشد برنامهها، پیچیدگی مدیریت وابستگیها، چرخههای استقرار و مالکیت کد نیز افزایش مییابد. اینجاست که معماری میکرو-فرانتاند درخشش خود را نشان میدهد و به تیمها اجازه میدهد برنامههای فرانتاند مستقل را بسازند، تست کنند و مستقر نمایند. در حالی که پیادهسازیهای اولیه به شیمهای iframe پیچیده یا راهحلهای بستهبندی زمان اجرا متکی بودند، Webpack 5 یک راهحل بومی و قدرتمند معرفی کرد: Module Federation.
این پست بررسی میکند که چگونه میتوان از پلاگین Module Federation در Webpack 5 برای ایجاد میکرو-فرانتاندهای واقعاً مقیاسپذیر و مستقل استفاده کرد که میتوانند بدون بار اضافی سنتی، بین برنامههای مختلف به اشتراک گذاشته و مصرف شوند.
Module Federation چیست؟
Module Federation مفهومی است که به برنامههای جاوااسکریپت اجازه میدهد کد را به صورت پویا از زمانهای اجرای دیگر (مرورگرها، سرورها یا Web Workers) در زمان اجرا وارد کنند. برخلاف بستهبندهای سنتی که وابستگیها را در زمان ساخت حل میکنند، Module Federation آنها را در زمان اجرا حل میکند. این بدان معناست که برنامه A میتواند کدی را که در برنامه B نوشته شده است اجرا کند، گویی که کد محلی است، به شرطی که هر دو برنامه در حال اجرا باشند.
مزایای کلیدی عبارتند از:
- استقرار مستقل: تیمها میتوانند میکرو-فرانتاند خاص خود را بهروزرسانی و مستقر کنند بدون اینکه برنامه میزبان را لمس کنند.
- وابستگیهای مشترک: کتابخانههای حیاتی مانند React یا Redux میتوانند بین برنامهها به اشتراک گذاشته شوند تا اندازه بسته کاهش یابد و یکپارچگی تضمین شود.
- بیطرفی فناوری: اگرچه Webpack ابزار اصلی است، اما پروتکل اجازه یکپارچهسازی با سیستمهای ساخت دیگر را میدهد، اگرچه Webpack بالغترین پیادهسازی را ارائه میدهد.
تنظیم برنامههای میزبان و ریموت
برای نمایش Module Federation، بیایید سناریویی با دو برنامه را فرض کنیم:
- برنامه میزبان (Host App): برنامه پوسته اصلی که رابط کاربری را هماهنگ میکند.
- برنامه ریموت (Remote App): یک ماژول خاص ویژگی (مانند داشبورد کاربر یا کامپوننت چت) که توسط میزبان مصرف خواهد شد.
مرحله ۱: نمایانسازی ماژولها در برنامه ریموت
اولین قدم این است که برنامه ریموت را پیکربندی کنیم تا کامپوننتهای خود را نمایان کند. در فایل webpack.config.js برنامه ریموت، ما ModuleFederationPlugin را وارد کرده و فیلد exposes را تعریف میکنیم.
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... other config
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./UserProfile': './src/components/UserProfile',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
}),
new HtmlWebpackPlugin({
template: './public/index.html'
})
]
};
در اینجا، ما ./UserProfile را نمایان میکنیم. ما همچنین وابستگیهای shared را پیکربندی میکنیم. با تنظیم singleton: true، اطمینان حاصل میکنیم که تنها یک نمونه از React در هر دو برنامه بارگذاری میشود که از عدم تطابق حالت جلوگیری کرده و اندازه پیکربندی را کاهش میدهد.
مرحله ۲: مصرف ماژولها در برنامه میزبان
در مرحله بعد، برنامه میزبان را پیکربندی میکنیم تا ماژول نمایانشده را مصرف کند. ما پیکربندی remote را در ModuleFederationPlugin برنامه میزبان اضافه میکنیم.
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
})
فیلد remotes به Webpack میگوید که فایل ورودی ریموت را کجا پیدا کند. میتوانید از یک URL ثابت یا یک نقطه پایانی پویا که پیکربندی را بر اساس محیط برمیگرداند، استفاده کنید. توجه داشته باشید که پیکربندی shared باید با برنامه ریموت مطابقت داشته باشد تا از سازگاری اطمینان حاصل شود.
وارد کردن پویا در React
با پیکربندی انجام شده، میتوانید کامپوننت ریموت را به صورت پویا با استفاده از API System.import (که توسط Webpack پلیفیل شده است) یا وارد کردنهای پویای استاندارد وارد کنید. در اینجا یک مثال در یک کامپوننت React آورده شده است:
import React, { lazy, Suspense } from 'react';
// Lazily load the remote component
const UserProfile = lazy(() => import('remoteApp/UserProfile'));
const HostComponent = () => {
return (
Loading... }>