Используйте Angular для создания корпоративного приложения ToB с архитектурой микроинтерфейса.

Angular.js

Эта статья на самом деле готовилась 11 месяцев, потому что, хотя мы начали использовать архитектуру микро-фронтенда Angular в начале года, продукт официально не выпущен, и его возможность не может быть проверена на практике в производственной среде. , наш продукт был официально недоступен.Пришло время поделиться нашим опытом на пути использования микро-фронтенда Angular (яма, на которую мы ступили), я надеюсь расти и развиваться вместе с сообществом Angular, если у вас есть определенное понимание микро-фронтенда и уже в проекте Пытался игнорировать предыдущие главы.

Что такое микрофронтенд

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

Так в чем же разница между микрофронтендом и микросервисом?

На следующем рисунке представлена ​​схематическая диаграмма микросервисов. Микросервисы в основном представляют собой бизнес-модули, которые разбиты по определенным правилам, независимо разрабатываются и развертываются независимо. После развертывания они маршрутизируются и перенаправляются через Nginx. модули Проблемы с вызовами, а также аутентификация, ведение журнала и даже присоединение к уровню шлюза

image.png

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

image.png

Зачем вам нужен Micro Front-end

  1. Увеличивается количество системных модулей, одно приложение становится раздутым, эффективность разработки низка, а скорость сборки падает;
  2. Расширение персонала требует, чтобы несколько фронтенд-команд разрабатывались и развертывались независимо друг от друга, если все они будут разрабатываться на одном складе, это принесет ряд проблем;
  3. Для решения устаревших систем новые модули должны использовать новейшие платформы и технологии, а старые системы продолжают использоваться.

Сравнение нескольких схем микроинтерфейса

Способ описывать преимущество недостаток степень сложности
переадресация маршрута Переадресация маршрутизации не является строго микроинтерфейсом, и навигация может быть разделена между несколькими подмодулями. Просто и легко реализовать Опыт не очень хороший, переключите приложение, чтобы обновить всю страницу 🌟
Вложенные фреймы Одно вложение iframe для каждого дочернего приложения Изоляция песочницы между приложениями Многократная загрузка скриптов и стилей 🌟🌟
состав времени сборки Независимое складирование, независимая разработка, общая упаковка во время строительства, слияние приложений Упрощение управления зависимостями и извлечение общих модулей Невозможно развернуть независимо, технологический стек и зависимые версии должны быть унифицированы. 🌟🌟
композиция во время выполнения Каждое подприложение создается независимо, а основное приложение отвечает за управление приложением, загрузку, запуск, выгрузку и механизм связи во время выполнения. Хороший опыт, настоящая независимая разработка, независимое развертывание Сложный, необходимо спроектировать загрузку, механизм связи, невозможно достичь полной изоляции, необходимо решить проблему конфликта зависимостей, конфликта стилей. 🌟🌟🌟
Web Components Каждое подприложение должно использовать технологию веб-компонентов для написания компонентов или использовать фреймворк для создания лицом к будущему Незрелый, нужно наступить на яму 🌟🌟🌟

Выше приведено лишь краткое сравнение нескольких методов реализации. Конечно, эти решения не являются взаимоисключающими. Какое решение выбрать, зависит от вашего бизнес-сценария. Следующие предпосылки имеют решающее значение для выбора технологии:

  • Является ли это монолитным приложением SPA?
  • Является ли технологический стек унифицированным и нуждается ли он в поддержке межкадровых вызовов?
  • Вам нужна полная изоляция между приложениями?

Мы — платформа SaaS корпоративного уровня. Это должно быть единое приложение SPA. Стек технологий полностью Angular. Нет необходимости в полной изоляции между приложениями. Вместо этого нам нужно использовать общие стили и компоненты, чтобы избежать повторной загрузки.

Итак, выбор:运行时组合строить планы.

Путь выбора передовых технологий Worktile

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

В Китае также есть много команд, у которых есть свои собственные микроинтерфейсные фреймворки, такие как единая спа-система с открытым исходным кодом.qiankun — возможно, самое полное решение для микроинтерфейса, которое вы когда-либо видели, и фодаляmooaИ бесчисленные внутренние решения (недавно Alifeibing также открыл исходный кодрешение микроинтерфейса icestark для больших рабочих столов, поддерживает только React и Vue)

Когда мы делаем технический выбор, первое соображениеsingle-spaа такжеmooa, single-spaЗрелость должна быть наивысшей, типовая документация идеальна,mooaФреймворк микроинтерфейса master-slave, созданный для Angular, наиболее соответствует нашему бизнесу и технологиям.После периода исследований мы, наконец, решили разработать набор библиотек микроинтерфейса, которые соответствуют нашим собственным (потому что он относительно прост, я не осмеливаюсь назвать его фреймворком), в основном потому, что наш бизнес имеет следующие требования, которые не выполняются или трудновыполнимы в вышеуказанных фреймворках, и даже требуют высокой степени кастомизации.

  • Продукт представляет собой структуру master-slave, а Portal включает в себя левую навигацию, уведомление о сообщениях и управление подприложениями.
  • Необходимость связи между несколькими подприложениями, главное приложение или подприложение должны открыть страницу сведений или перейти по маршруту других подприложений.
  • Определенная страница подприложения A может загружать компонент подприложения B.
  • Основываясь на двух вышеуказанных характеристиках, необходимо обеспечить режим сосуществования, то есть, хотя в настоящее время отображается приложение B, необходимо обеспечить возможность нормального вызова приложения A. Если оно уничтожено, его нельзя вызвать другими приложениями.
  • Требуется предварительная загрузка
  • Стили подприложений также необходимо загружать независимо.
  • Маршрутизация, будь то в основном приложении или подприложении, опыт маршрутизации должен соответствовать одному приложению.

я побежалsingle-spaа такжеmooaПримеры в основном представляют собой простые экраны рендеринга.Как только вам нужно будет соответствовать вышеуказанным характеристикам, вам все равно нужно будет изменить много вещей.mooaРеализация должна быть более полной и подходящей для нас, но в ее примере есть некоторые проблемы с роутингом, страница прыгает, а роутинг не меняется, упаковка отказалась от Angular CLI, и ссылка на уровень кодаsingle-spaAPI можно еще раз упростить.Поскольку он заточен под Angular, думаю, он должен больше соответствовать Angular.Конечно, не исключено, что автор захочет позже поддерживать React и Vue.Неоспорим тот факт, чтоphodalЯ хорошо разбираюсь в микро-фронтендах и написал много хороших статей о микро-фронтендах.microfrontends, и даже опубликовал единственную книгу по микро-фронтендам «Front-end Architecture — From Getting Started to Micro-frontends», я также использовал многие её идеи и методы реализации для справки при реализации микро-фронтендов.

Создание микрофронтенда с помощью Angular

Использование Angular для реализации микроинтерфейса на самом деле сложнее, чем React и Vue, потому что Angular включает компиляцию AOT, модуль, Zone.js, совместное использование сервисов и т. д. страница.

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

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

  1. Первое решение состоит в том, чтобы рассматривать каждый подмодуль как функциональный модуль, а затем упаковывать и компилировать его вместе с основным приложением при упаковке.Это самое простое, но это не может быть развернуто отдельно, и каждое развертывание представляет собой полное обновление.
  2. Второе решение состоит в том, чтобы рассматривать подмодуль как функциональный модуль, загружать подмодуль через SystemJsNgModuleLoader в основном приложении, а затем компилировать и запускать (Примечание: в новой версии от SystemJsNgModuleLoader отказались).
  3. Третье решение состоит в том, что каждый подмодуль является независимым приложением.Как и основное приложение, он имеет свой AppModule и маршрутизацию.При выборе этого решения необходимо решить проблему синхронизации маршрутизации нескольких приложений.Кроме того, текущая библиотека зависимостей Angular: Если ее нельзя использовать непосредственно во время выполнения, каждое подприложение необходимо скомпилировать вместе, и невозможно извлечь общедоступные библиотеки зависимостей (могут быть другие решения)
  4. Четвертый вариант — скомпилировать все подмодули в веб-компоненты для использования.Я давно не изучал его глубоко.Выбрать этот вариант для прямого использования компонентов точно не проблема, но я не знаю, как быть с маршрутизация после использования веб-компонентов.

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

image.png

Регистрация приложений, загрузка, механизм уничтожения

Это основа и ядро ​​всех микро-фронтенд-приложений, но я думаю, что это самое простое и легкое в реализации.Главное, что нужно сделать, это:

  • Обеспечьте функцию динамической загрузки статических ресурсов
  • Настройте правила субприложения, в том числе: имя приложения, префикс маршрутизации, файл статического ресурса.

    this.planet.registerApps([
      {
          name: 'app1',
          hostParent: '#app-host-container',
          routerPathPrefix: '/app1',
          selector: 'app1-root',
          scripts: ['/static/app1/main.js'],
          styles: ['/static/app1/styles.css']
      },
      // ...
    ]);
    
  • Загрузка приложения: найдите соответствующее подприложение в соответствии с URL-адресом текущей страницы, затем загрузите статические ресурсы приложения и вызовите предопределенную функцию запуска для непосредственного запуска приложения.В Angular это запуск корневого модуля.platformBrowserDynamic().bootstrapModule(AppModule).

  • Предварительная загрузка приложения: другие приложения будут предварительно загружены после рендеринга и запуска текущего приложения и не будут отображаться.
  • Уничтожить использование приложенияappModuleRef.destroy();

В основном достаточно иметь дело с простыми сценариями в соответствии с вышеуказанными шагами, но если вы хотите, чтобы приложения сосуществовали, это будет по-другому.bootstrappedГосударство скрыто, а не уничтожено, толькоActiveСтатус заявки будет отображаться на текущей странице.

маршрутизация

Поскольку каждое подприложение выбирается как независимое приложение Angular, а несколько подприложений могут сосуществовать одновременно, синхронизация маршрутизации нескольких приложений и переходы становятся проблемой, и необходимо поддерживать переходы маршрутизации между приложениями и связью. между приложениями, рендеринг компонентов и другие сценарии. Я думаю, что маршрутизация — самая сложная проблема, с которой мы сталкиваемся в архитектуре микроинтерфейса.

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

{
        path: '**',
        component: EmptyComponent
}

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

  • Когда основной маршрут приложения переключается, найдите все запущенные в данный момент подприложения, используйтеrouter.navigateByUrlСинхронный прыжок
  • Когда маршрут подприложения переключается, основной маршрут приложения синхронизируется, и другие подмаршруты в состоянии запуска синхронизируются одновременно.

Я просмотрел множество фреймворков для микроинтерфейса, в том числеsingle-spa, в основном маршрутизацией не занимаются, и разработчику полностью предоставлено заполнить дыру.single-spaПример Angular в основном уничтожает приложение Angular после переключения.Поскольку нет сосуществования, нет необходимости решать проблему маршрутизации нескольких приложений.Конечно, как независимое от фреймворка решение для микроинтерфейса, он может сделать только этот шаг , Бар.

После того, как движок рендеринга Ivy будет официально выпущен, подприложения могут быть скомпилированы в модули, которые можно запускать напрямую.Если все приложение имеет только один маршрут, это будет намного проще.

Общие глобальные сервисы

Для некоторых глобальных данных мы обычно храним их в сервисе, а затем подприложения могут делиться ими напрямую, например:当前登录用户,多语言服务И т. д., простой обмен данными можно монтировать прямо в окно.Чтобы каждое подприложение использовало один и тот же глобальный сервис и сервис в модуле, мы инстанцируем эти сервисы в основном приложении, но затем в AppModule каждого суб-приложение.Используйте предоставление для сброса значения основного приложения.Конечно, эти бизнес-разработчики, которым не нужно устанавливать субприложения самостоятельно, были инкапсулированы в библиотеке бизнес-компонентов и настроены глобально.

{
  provide: AppContext,
  useValue: window.portalAppContext
}

связь между приложениями

Существует много способов взаимодействия между приложениями, мы используем базовые возможности браузера.CustomEvent, поверх этого инкапсулированногоGlobalEventDispatcherСлужба взаимодействует (конечно, вы также можете использовать ее для монтирования глобального объекта на объекте окна), сценарий заключается в том, что субприложение хочет открыть страницу сведений другого субприложения.

// App1
globalEventDispatcher.dispatch('open-task-detail', { taskId: 'xxx' });

// App2
globalEventDispatcher.register('open-task-detail').subscribe((payload) => {
    // open dialog of task detail
});

Компоненты вызывают друг друга между приложениями

в нашем敏捷开发В подпродукте должна отображаться страница сведений о пользовательской истории.测试管理Связанные тестовые случаи и статус выполнения теста приложения, затем компонент списка тестовых случаев помещается в测试管理подприложение является наиболее подходящим, тогда страница сведений о пользовательской истории должна быть в敏捷开发Как загрузить в приложение测试管理Определенный компонент приложения является проблемой.

Этот кусок используетсяAngular CDK 中的 DomPortalOutletДинамически создавать компоненты и указывать рендеринг в контейнере, что гарантирует, что создание этого динамического компонента все еще测试管理Модули просто рендерятся в других приложениях.

const portalOutlet = new DomPortalOutlet(container, componentFactoryResolver, appRef, injector);
const testCasesPortalComponent = new ComponentPortal(TestCasesComponent, null);
portalOutlet.attachComponentPortal(testCasesPortalComponent);

Инжиниринг

Использование микроинтерфейса для разработки приложений должно решать не только технические проблемы Angular, но и некоторые инженерные проблемы, такие как разработка, совместная работа и развертывание, например:

  • Извлечение общей библиотеки зависимостей
  • Как начать разработку локально
  • Как упаковать и развернуть и как уведомить основное приложение о сгенерированном файле хэш-ресурса

Применение извлечения общедоступной библиотеки зависимостей, чтобы избежать повторной упаковки библиотек классов и уменьшить объем упаковки, что требует реализации пользовательской конфигурации Webpack.Сначала мы полностью настроили Webpack для упаковки приложений Angular.Как только мы это сделаем, мы потеряем многие удобные функции, предоставляемые CLI , библиотека классовangular-builders, его роль на самом деле состоит в том, чтобы объединить пользовательскую конфигурацию Webpack с конфигурацией Webpack, сгенерированной Angular CLI, так что нужно написать лишь небольшое количество пользовательской конфигурации, а остальные по-прежнему используют функцию упаковки CLI, и почти должны напиши сам Аналогичный инструмент тоже.
Поместите необходимые публичные зависимости в основное приложениеscripts, а затем настроить в подприложенииexternals,Например:moment lodash rxjsтакая библиотека.

const webpackExtraConfig = {
    optimization: {
        runtimeChunk: false // 子应用一定要设置 false,否则会报错
    },
    externals: {
        moment: 'moment',
        lodash: '_',
        rxjs: 'rxjs',
       'rxjs/operators': 'rxjs.operators',
        highcharts: 'Highcharts'
    },
    devtool: options.isDev ? 'eval-source-map' : '',
    plugins: [new WebpackAssetsManifest()]
};
return webpackExtraConfig;

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

Конфигурация локальной разработкиproxy.conf.jsПрокси-доступ к файлам ресурсов каждого вложенного приложения, включая вызовы API.

Библиотека микрофронтенда на основе Angular ngx-planet

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

Адрес репозитория на гитхабе:ngx-planet
Онлайн-демонстрация:planet.ngnice.com

Не осмеливаюсь сказать "самое законченное решение для микро-интерфейса, которое вы когда-либо видели", но, по крайней мере, сообщество Angular видело решения, которые можно полноценно использовать в продакшн-средах.API соответствует Angular Style, и многие отечественные производители в основном игнорировали микроинтерфейсные решения.С существованием фреймворка Angular четыре подпродукта R&D Worktile полностью основаны наngx-planetСтроительство и развитие, после почти года хождения по яме и практики, в основном полностью доступны.

image.png

Я надеюсь, что у сообщества Angular будет больше решений для микроинтерфейса, и мы вместе добьемся прогресса. С нашим решением определенно много проблем. Мы также приветствуем ваши предложения и жалобы по улучшению. Мы также продолжим работу над микроинтерфейсом Angular. Ищете библиотеку микроинтерфейса для Angular, попробуйте ngx-planet.

В дальнейшем будем исследовать схемы оптимизации и улучшения под движок рендеринга Ivy.


Официальный сайт Worktile:worktile.com

Автор этой статьи: Сюй Хайфэн, старший инженер Worktile

Статья впервые появилась в "Официальный блог Worktile"Пожалуйста, укажите источник.