Термины и определения

Сначала терминология, чтобы мы были в одном контексте.

Host (consumers) — это бандл, который первый инициализировался во время загрузки страницы. Это и есть наше корневое приложение, которое подтягивает другие части. 

Remote (consumable) — другой бандл, чьи некоторые части может импортировать host. Host запрашивает у remote шареные компоненты.

Omnidirectional host — это бандл, который одновременно может быть и host, и remote. Он может быть и SPA-приложением, а может просто раздавать общие компоненты.

Exposed modules. Модули, которые будут доступны другим приложением для импорта, например, компоненты, картинки или стили.

Shared modules. Модули, которые могут быть общими для всего приложения, например, React.

Как webpack собирает модули?

Чтобы понять как работает MF, важно уяснить как Webpack работает с модулями после сборки. Для этого я подготовил небольшой пример. Вы можете скачать репозиторий и самостоятельно ознакомиться с итогом сборки.

Пример. У нас имеется корневой файл, который импортирует функцию action из модуля, получает новое значение, а затем экспортирует полученное значение.

 
import action from './module';
 
const value = action();
 
export default value;

Выполним сборку Webpack. В итоговом файле мы видим несколько вызовов eval. Это потому что Webpack хранит наш код в виде строки (только в режиме разработки). Но если убрать все лишнее мы получим следующее:

Условно, Webpack добавляет наш модуль в modules scope (не путать с областью видимости) в специальный массив __webpack_exports__

__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, { "default": () => (action) });
 
function action() {return 'value';};

А вот тут Webpack успешно достает наш модуль из modules scope и присваивает переменной _module__WEBPACK_IMPORTED_MODULE_0__ для дальнейшего использования.

__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
       "default": () => (__WEBPACK_DEFAULT_EXPORT__)
});
 
var _module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/module.js\");
         
const value = (0,_module__WEBPACK_IMPORTED_MODULE_0__["default"])();
         
const __WEBPACK_DEFAULT_EXPORT__ = (value);

Единственное, что нужно уяснить из приведенного примера — это то, что Webpack собирает все наши модули в некий массив modules scope.

Схема работы MF. Теперь давайте взглянем на следующую схему работы MF. 

  • Слева желтым — host, то приложение которое будет получать общие модули.
  • Справа оранжевым — Remote контейнер, который раздаёт общие модули.
  • Посередине — share scope. Это как раз наш modules scope.

Когда host запросит у контейнера шареные компоненты, они попадут в тот самый скоуп, о котором чуть выше говорил (в примере). Webpack даже не будет знать, что эти компоненты были откуда-то загружены, и будет использовать так, будто они всегда были в основной сборке.

Конечно, приведенный выше пример очень условный, в реальности все намного сложнее, но главное понять принцип.

Загружаются Remote модули, обычно, с помощью динамического import() — прямо в рантайме.

Полезные ссылки. Подробнее узнать как работает WMF можно из расширенных гайдов:

Демо

Чтобы лучше понять суть, я подготовил еще немного практики. Ниже ссылки на 2 репозитория. В одном приложение Host, во втором Remote. Host загружает у Remote общий компонент.

Скачайте репозитории, установите зависимости, запустите оба сервера и посмотрите как они работают в связке. Экспериментируйте - отключите Remote приложение, посмотрите как будет работать Host, создайте свои общие компоненты.

Сборку я намеренно упростил, чтобы понять принцип и не усложнять контекст.