В пятой версии сборщика Webpack появился набор плагинов для обмена модулями между Javascript приложениями.
Эта статья — краткое описание и пример на основе двух небольших приложений построенных на фреймворке ReactJS.
Плагин Module Federation позволяет приложению экспортировать один или несколько модулей в отдельный JS файл. Отличный способ строить микрофронтенд приложения. Сторонние приложения могут импортировать себе готовые модули, это могут быть например реакт компоненты. Причём, импорт зависимостей Webpack берёт на себя. Отличие от NPM в том, что импорт в runtime.
Приложение-источник в конфиге webpack явно указывает:
- модули для экспорта
- имя файла экспорта например export.js
- зависимости, которые экспортировать не нужно например export.js
В HTML приложения-получателя импортируется JS файл приложения-источника как обычный JS скрипт:
<script src="http://source-app.com/export.js">
А в webpack конфиге, указывается какие модули брать из файла.
Вот как это выглядит
Приложение-источник должно прописать экспорт в настройках webpack.config.js
В настройках плагина указываем название экспортируемого контейнера, какие модули в него войдут и как будет называться файл.
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
...
plugins: [
new ModuleFederationPlugin({
name: 'home',
library: { type: 'var', name: 'home' },
filename: 'export.js',
exposes: {
ProductCarousel: './src/ProductCarousel'
},
shared: ['react', 'react-dom', '@material-ui/core', '@material-ui/icons']
}),
]
};
Приложение-получатель
- В index.html импортирует js файл приложения-источника
пример
<!DOCTYPE html> <html lang="en"> <head> <script src="http://source-app.com/export.js"></script>
- В webpack.config.js, в настройках плагина указываем название нужного контейнера, плагин сам найдёт его в JS файле
пример конфига
const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { ... plugins: [ new ModuleFederationPlugin({ remotes: {home: 'home' } //указываем контейнер }), ] };
- В JSX импортируем модуль из нужного контейнера, как будто это обычный локальный модуль
пример кода
import React from 'react'; import ProductCarousel from 'home/ProductCarousel'; function App() { return ( <ProductCarousel /> ); } export default App;
Вот более подробный пример со ссылкой на репозиторий
- Склонируйте репо github.com/jherr/wp5-intro-video-code
- Установите зависимости и запустите проект
yarn install yarn start
- Запустятся три простых реакт приложения на разных портах
home (packages/home) localhost:3001/ search page (packages/search) localhost:3002/ nav (packages/nav) localhost:3003/ - Обратите внимание на вебпак в home: packages/home/webpack.config.js, там уже настроен экспорт компонента ProductCarousel
new ModuleFederationPlugin({ name: 'home', library: { type: 'var', name: 'home' }, filename: 'remoteEntry.js', //в этом файле будет весь экспорт приложения для внешних получателей remotes: { nav: 'nav' }, exposes: { ProductCarousel: './src/ProductCarousel' //экспортируем один модуль, назовём его ProductCarousel }, shared: ['react', 'react-dom', '@material-ui/core', '@material-ui/icons'] //это должно быть у получателя }),
Вот как выглядит этот компонент в приложении home (я обвёл его красной рамкой)
- Теперь импортируем этот компонент в приложении SearchPage
- В packages/search/public/index.html добавьте импорт JS файла который экспортирует home, он называется remoteEntry.js (обратите внимание на порт 3001 — это порт приложения home)
<!DOCTYPE html> <html lang="en"> <head> <script src="http://localhost:3003/remoteEntry.js"></script> <script src="http://localhost:3001/remoteEntry.js"></script> </head> <body> <div id="root"></div> </body> </html>
- В настройках вебпак опишите контейнер который мы импортируем. Для этого в remotes просто добавьте название контейнера который вы импортируете, ModuleFederation сам найдёт его. (На самом деле для импорта никакие другие настройки плагина для импорта не нужны, вы можете удалить все настройки кроме remotes и импорт останется рабочим)
module.exports = { ... plugins: [ new ModuleFederationPlugin({ name: 'search', library: { type: 'var', name: 'search' }, filename: 'remoteEntry.js', remotes: { nav: 'nav', home: 'home' }, exposes: { }, shared: ['react', 'react-dom', '@material-ui/core', '@material-ui/icons'] }),
- Теперь просто втавьте этот компонент в коде SearchPage, packages/search/src/App.jsx
import ProductCarousel from 'home/ProductCarousel'; function App() { return ( <Container fixed> <CssBaseline /> <Header /> <Typography variant="h3"> Search Page. </Typography> <ProductCarousel /> </Container> ); }
- В packages/search/public/index.html добавьте импорт JS файла который экспортирует home, он называется remoteEntry.js (обратите внимание на порт 3001 — это порт приложения home)
- Остановите скрипт yarn и запустите заново, что бы изменения вебпак вступили в силу. Откройте приложение SearchPage и обратите внимание что там появилась карусель из приложения home localhost:3002/
Было Стало
Полезные ссылки
- Официальное описание вебпак плагина здесь webpack.js.org/concepts/module-federation
- Статья с примером indepth.dev/webpack-5-module-federation-a-game-changer-in-javascript-architecture
- Отличное видео с объяснением и примером кода youtu.be/D3XYAx30CNc
Автор: Артём