Предварительные требования

Этот блог требует от вас теоретического понимания того, что такое микро-интерфейсы и почему они вообще существуют. Если слово микро-интерфейс впервые коснулось клеток вашего мозга, я бы попросил вас прокрутить мозг над этим видео от Джоэла Деннинга.

Сценарий, поскольку мы люди

Представьте себе экосистему микро-интерфейса, в которой несколько приложений Angular объединены на одной странице для создания удобного взаимодействия с пользователем, как показано ниже.

В приведенном выше сценарии Angular Application 1 и Angular Application 2 загружаются на одной вкладке браузера. Возникает вопрос, сколько раз фреймворк / компилятор Angular и другие распространенные библиотеки загружались в браузер. Вы угадали, ответ - дважды. Это связано с тем, что webpack объединяет все зависимости при объединении приложения, и то же самое загружается в браузер каждый раз, когда загружается приложение Angular.

Давайте посмотрим все это в действии

Чтобы продемонстрировать это, давайте создадим в нашей системе экосистему микро-интерфейса. Я бы посоветовал вам писать код, так как это поможет вам извлечь уроки из этого блога.

Шаг 1. Установка Angular CLI

Давайте установим зависимости, необходимые для создания экосистемы микро-интерфейса с использованием single-spa framework, Angular и SystemJS.

Выполните следующую команду в каталоге, в котором вы хотите настроить экосистему микро-интерфейса:

yarn global add @angular/[email protected]

or

npm install --global @angular/[email protected]

Шаг 2: Создание первого Micro Frontend: angular-application-one

Поскольку командная строка angular установлена, давайте создадим приложение angular.

Выполните следующую команду в каталоге, в котором вы хотите установить первое приложение экосистемы микро-интерфейса:

ng new angular-application-one --routing --prefix ng-app-one

Когда вас попросят указать формат таблицы стилей, выберите CSS.

Шаг 3. Добавление поддержки одного спа: angular-application-one

Чтобы приложение, созданное с помощью интерфейса командной строки angular, поддерживало экосистему микро-интерфейса с одним спа-центром и загружалось как встроенный в браузер модуль, нам необходимо выполнить несколько шагов, упомянутых здесь.

Перейдите в папку angular-application-one и выполните следующую команду:

ng add single-spa-angular

Он должен задать вам пару вопросов, ответьте на них следующим образом:

Далее перейдите по этой ссылке и настройте маршруты в angular-application-one.

Шаг 4: Создание еще одного микро-интерфейса: angular-application-two

Выполните следующую команду в каталоге, в котором вы хотите настроить вторую экосистему микро-интерфейса (используйте тот же каталог, что и на шаге 2)

ng new angular-application-two --routing --prefix ng-app-two

Когда вас попросят указать формат таблицы стилей, выберите CSS.

Шаг 5: Добавление поддержки single-spa: angular-application-two

Перейдите в папку angular-application-two и выполните следующую команду:

ng add single-spa-angular

Он должен задать вам пару вопросов, ответьте так же, как это было сделано на шаге 3.

Далее перейдите по этой ссылке и настройте маршруты в angular-application-two.

Шаг 6: Создание корневого приложения для одного спа

Установите глобальные зависимости для CLI create-single-spa.

yarn global add [email protected]

or

npm install --global [email protected]

Выполните следующую команду в каталоге, который использовался на шаге 2.

create-single-spa

Он должен задать вам пару вопросов, ответьте на них следующим образом:

После завершения установки ваша текущая структура каталогов должна выглядеть следующим образом:

code/ 
     angular-application-one 
     angular-application-two 
     wrapper-application

В приложении-оболочке установите следующие зависимости.

yarn add core-js

or

npm install --save core-js

Шаг 7. Запуск микро-интерфейсов

Выполните следующую команду в каталоге angular-application-one

yarn serve:single-spa

Приведенная выше команда должна запускать angular-application-one на http: // localhost: 4200 /

Что ж, подождите, не заходите прямо сейчас в браузер, вы ничего не увидите, так как это модуль в браузере, который можно использовать вместе с single-spa, а не индивидуально.

Затем перейдите в angular-application-two / package.json и измените build: single-spa и serve: single-spa следующим образом:

"build:single-spa": "ng build --prod --deploy-url http://localhost:4300/",
"serve:single-spa": "ng serve --disable-host-check --port 4300 --deploy-url http://localhost:4300/ --live-reload false"

Выполните следующую команду в каталоге angular-application-two

yarn serve:single-spa

Приведенная выше команда должна запускать angular-application-two на http: // localhost: 4300 /

Что ж, подождите, не заходите прямо сейчас в браузер, вы ничего не увидите, так как это модуль в браузере, который можно использовать вместе с single-spa, а не индивидуально.

Шаг 8: добавление микро-интерфейсов в экосистему

Затем перейдите в wrapper-application / src / mfe-root-config.ts и добавьте следующую строку кода для регистрации приложений микро-интерфейса: angular-application-one и angular-application-two.

registerApplication({  
         name: '@mfe/ng-app-one',
         app: () => System.import('@mfe/ng-app-one'),  
         activeWhen: ['/'],
});
registerApplication({  
         name: '@mfe/ng-app-two',  
         app: () => System.import('@mfe/ng-app-two'),  
         activeWhen: ['/'],
});

А затем добавьте импорт вверху следующим образом:

import 'core-js/proposals/reflect-metadata';

Теперь файл mfe-root-config.ts должен выглядеть так:

import { registerApplication, start } from 'single-spa';
import 'core-js/proposals/reflect-metadata';
registerApplication({  
         name: '@mfe/ng-app-one',
         app: () => System.import('@mfe/ng-app-one'),  
         activeWhen: ['/'],
});
registerApplication({  
         name: '@mfe/ng-app-two',  
         app: () => System.import('@mfe/ng-app-two'),  
         activeWhen: ['/'],
});
start({  urlRerouteOnly: true,});

Затем перейдите в wrapper-application / src / index.ejs и добавьте следующий код после строки номер 55:

"@mfe/ng-app-one": "http://localhost:4200/main.js",          "@mfe/ng-app-two": "http://localhost:4300/main.js"

В том же файле добавьте следующий код в основные теги в теле

<main>
     <div id="single-spa-application:@mfe/ng-app-one"></div>                  <div id="single-spa-application:@mfe/ng-app-two"></div> 
</main>

Затем закомментируйте метатег, определяющий политику безопасности контента в том же файле.

Также раскомментируйте строку 67, которая загружает zone.js в экосистему в том же файле.

Шаг 9: Запуск приложения-оболочки

Выполните приведенную ниже команду в приложении-оболочке

yarn start

Посетите http: // localhost: 9000 / в своем браузере и вы должны увидеть два приложения angular, запущенных на разных портах, но загруженных вместе на одной вкладке браузера. У вас готов минималистичный микро-интерфейс.

Похлопайте себя по плечу, если вы добрались сюда и правильно подали заявку. Хороший товарищ по работе !!

Шаг 10: обнаружение вздутия

Обратите внимание на текущий размер сборок для angular-application-one и angular-application-two, которые загружаются в браузере, перейдя на вкладку сети, перезагрузив приложение и отметив текущий размер загружаемых файлов main.js с локального хоста: 4200 и локального хоста: 4300. Вы должны увидеть что-то похожее на это.

Что ж, загрузка 8 МБ JavaScript для рендеринга автоматически сгенерированного приложения CLI неприемлема. Одной из основных причин такого размера является компилятор angular и другие компоненты фреймворка Angular, которые загружаются в браузере каждый раз, когда загружается микро-интерфейс.

Здесь, можно сказать, Мех! Вот как это работает, и пусть идет производство, или, как сказал Махатма Ганди: «Будьте тем изменением, которое вы хотите увидеть в мире».

Возможно ли это?

Единственный способ избежать этого раздувания - загрузить компилятор Angular и другие компоненты / библиотеки фреймворка только один раз. И то же самое можно было бы передать всем микро-интерфейсам, загруженным в экосистему. Это позволит микро-интерфейсам загружать специфичный для приложения код, а зависимости затем могут быть предоставлены экосистемой. Но как?

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

Roll, Camera, Optimization.

Шаг 1. Устранение зависимостей: angular-application-one

Откройте angular-application-one / extra-webpack.config.js и добавьте следующий код после определения константы singleSpaWebpackConfig и перед оператором return:

singleSpaWebpackConfig.externals = {    
"@angular/animations": "@angular/animations",
"@angular/common": "@angular/common",
"@angular/compiler": "@angular/compiler",
"@angular/core": "@angular/core",
"@angular/forms": "@angular/forms",
"@angular/platform-browser": "@angular/platform-browser",    "@angular/platform-browser-dynamic": "@angular/platform-browser-dynamic",    
"@angular/router": "@angular/router",
rxjs: "rxjs", 
"rxjs/operators": "rxjs/operators",
tslib: "tslib", 
"single-spa": "single-spa",  
};

Остановите работающее приложение angular-application-one и снова обслужите его, используя следующую команду:

yarn serve:single-spa

Посетите http: // localhost: 9000 / еще раз, открыв вкладку сети.

Вы должны заметить файл main.js, загруженный с localhost: 4200. Ниже будут ваши выводы:

Обратите внимание на разницу в размере файла main.js, выделенного синим цветом. Размер файла до оптимизации (внешний вид Webpack) составлял около 4 МБ, а теперь он уменьшился до 75 КБ. Что ж, это секрет внешнего вида Webpack, однако эта магия сделала наше приложение невидимым, поскольку вы можете заметить, что мы больше не можем видеть angular-application-one в браузере. Давайте добавим последний кусочек головоломки.

Шаг 2. Загрузка внешних зависимостей, как в модулях браузера.

Перейти к строке номер 35 в файле wrapper-application / index.ejs. Тег скрипта, который мы видим там, мы бы добавили все внешние зависимости в виде модулей в браузере.

Добавьте следующее после зависимости «single-spa». Тег скрипта должен выглядеть так.

<script type="systemjs-importmap">
     {        
          "imports": {
               "single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
          "@angular/common": "https://cdn.jsdelivr.net/npm/@angular/[email protected]/bundles/common.umd.js",
          "@angular/core": "https://cdn.jsdelivr.net/npm/@angular/[email protected]/bundles/core.umd.js",
          "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@angular/[email protected]/bundles/platform-browser.umd.js",                 "@angular/platform-browser-dynamic": "https://cdn.jsdelivr.net/npm/@angular/[email protected]/bundles/platform-browser-dynamic.umd.js",             "@angular/router": "https://cdn.jsdelivr.net/npm/@angular/[email protected]/bundles/router.umd.js",          
"rxjs": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/rxjs.umd.js",          "rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/rxjs-operators.min.js",          "@angular/compiler": "https://cdn.jsdelivr.net/npm/@angular/[email protected]/bundles/compiler.umd.js",          
"tslib": "https://cdnjs.cloudflare.com/ajax/libs/tslib/1.13.0/tslib.min.js"           }      
}   
 </script>

Теперь остановите текущее запущенное приложение-оболочку и снова обслужите его, используя команду:

yarn start

Теперь вы должны увидеть, как angular-application-one работает в браузере с использованием общих зависимостей, тогда как angular-application-two использует свои собственные зависимости.

Шаг 3: Устранение зависимостей: angular-application-two

Откройте angular-application-two / extra-webpack.config.js и добавьте следующий код после определения константы singleSpaWebpackConfig и перед оператором return:

singleSpaWebpackConfig.externals = {
"@angular/animations": "@angular/animations", 
"@angular/common": "@angular/common",
"@angular/compiler": "@angular/compiler",
"@angular/core": "@angular/core",    
"@angular/forms": "@angular/forms",    
"@angular/platform-browser": "@angular/platform-browser",    "@angular/platform-browser-dynamic": "@angular/platform-browser-dynamic",    
"@angular/router": "@angular/router",
rxjs: "rxjs", 
"rxjs/operators": "rxjs/operators",
tslib: "tslib",
"single-spa": "single-spa",  
};

Остановите работающее приложение angular-application-two и снова обслужите его, используя следующую команду:

yarn serve:single-spa

Посетите http: // localhost: 9000 / еще раз, открыв вкладку сети.

Вы должны заметить файл main.js, загруженный с localhost: 4300. Ниже будут ваши выводы

Обратите внимание на разницу в размере файла main.js, выделенного синим цветом. Размер файла до оптимизации (внешний вид Webpack) составлял около 4 МБ, а теперь он уменьшился до 75 КБ.

Ура, наконец-то нам есть чем гордиться!

Рад видеть, что вы пришли сюда. Цените ваше присутствие ума и преданность делу 😃

Наконец, обращение к дьяволу

Есть предостережения, связанные с каждой оптимизацией, и совместное использование зависимостей не является исключением. Вот несколько указателей, о которых вам следует знать, прежде чем применять общие зависимости в своей архитектуре.

  • SystemJS загружает общие зависимости в виде модулей в браузере, поэтому возможности Webpack по встряхиванию дерева отсутствуют. Это будет означать, что в случае, если библиотека частично используется всеми микро-интерфейсами, неиспользуемый JS все равно будет загружен в браузер, поскольку модули в браузере не предоставляют возможности встряхивания дерева.
  • Добавление библиотек в качестве встроенных модулей браузера аналогично наличию тега сценария для каждой зависимости, который делает запрос GET для получения javascript, расположенного в расположении CDN. Следовательно, следует с осторожностью принимать решение о том, сколько библиотек будет использоваться совместно и загружаться как модули в браузере по сравнению с модулями времени сборки.
  • В случае, если внешняя библиотека используется менее чем 60% микро-интерфейсов в экосистеме, предпочтите, чтобы они были построены во время сборки, поскольку в конечном итоге мы будем загружать библиотеку как в браузере, даже если это не требуется для остальных 40%. модули. Особенно, когда к этим 60% модулей обращаются один или два раза на пути пользователя.
  • Совместное использование зависимостей может в долгосрочной перспективе укусить, когда в разработку микро-интерфейсов вовлечена огромная команда, поскольку все команды, участвующие в разработке, должны знать о конкретной версии внешней зависимости, используемой во всех микро-интерфейсах. Как и в нашем примере, мы разделили фреймворк Angular со всеми микро-интерфейсами, но это может быть проблемой в случае, если команда запускает новый микро-интерфейс, и они хотят принять более последнюю версию Angular, где общие зависимости Angular 8 тогда окажется техническим отставанием, а не оптимизацией.

Совместное использование зависимостей между микро-интерфейсами должно быть хорошо продуманным решением и должно рассматриваться в индивидуальном порядке, а не рассматриваться как серебряная пуля и совместное использование всех возможных зависимостей.

Осторожно, спойлеры !!

О, я забыл упомянуть об этом, если вы используете движок рендеринга Angular Ivy в экосистеме микро-интерфейса, запрещено делиться зависимостями. Вот подробное объяснение от Джоэла Деннинга.