Redux в Angular. Генерация приложения с микрофронтендами.
В данной статье описывается процесс создания приложения с микрофронтендами.
Для создания демо приложения будем использовать Nx.
Так как подразумевается использование 3 разных state management
’ов, то для простоты демонстрации используем микрофронтенды.
Nx из коробки умеет создавать подобного рода без дополнительных телодвижений, так что процесс аналогичен стандартным использованием
angular-cli
.
Для работы будем использовать уже ранее созданный nx workspace
с предустановленным Angular
— angular-samples.
Для создания нового workspace
запустите команды:
yarn create nx-workspace samples --packageManager=yarn
Если в процессе создания пространства был выбран пустой проект, то необходимо добавить поддержку Angular
:
yarn add -D @nrwl/angular
Создадим одно shell
приложение redux-dashboard
и три remote
приложения redux-ngrx
, redux-ngxs
и redux-akita
.
Для этого достаточно запустить команду:
nx g @nrwl/angular:host redux/dashboard --remotes=redux/ngrx,redux/ngxs,redux/akita
В результате будут созданы следующие приложения:
nx-workspace
├── apps
│ └── redux
│ ├── akita
│ ├── dashboard
│ ├── ngrx
│ └── ngxs
├── angular.json
└── ...
Для запуска используется команда:
nx serve redux-dashboard --devRemotes=redux-ngrx,redux-ngxs,redux-akita
Запуститься четыре приложения:
localhost:4200
—redux-dashboard
, основноеshell
приложение;localhost:4201
—redux-ngrx
, remote приложение cNgrx
;localhost:4202
—redux-ngxs
, remote приложение cNgxs
;localhost:4203
—redux-akita
, remote приложение cAkita
.
Более подробно с использованием и настройкой микрофронтендов можно ознакомиться в документации.
Демо готового приложения — redux.fafn.ru.
Особенности разработки микрофронтендов в Nx
Есть ряд нюансов при разработке библиотек в Nx
, которые планируются использоваться в микрофронтендах.
Создание core модуля
Так как точкой входа в remote
приложении является RemoteEntryModule
, который подключается lazy loading
, все библиотеки размещенные в AppModule
будут не доступны в RemoteEntryModule
.
Для решения данной проблемы можно создать AppCoreModule
, который будет содержать общие библиотеки для RemoteEntryModule
и AppModule
.
Пример для ngrx
приложения:
@NgModule({
imports: [LayoutModule, HammerModule],
providers: [
{
provide: REDUX_TYPE,
useValue: 'NGRX',
},
{
provide: LOCALE_ID,
useValue: 'ru',
},
{
provide: DEFAULT_CURRENCY_CODE,
useValue: 'RUB',
},
{
provide: MAT_DATE_LOCALE,
useValue: 'ru-RU',
},
{
provide: ENVIRONMENTS,
useValue: environment,
},
],
})
export class AppCoreModule {}
Экспорт shared компонентов
Первая особенность связанная с экспортом модулей. Для корректной работы экспортируемых компонентов необходимо в библиотеках nx
экспортировать не только модули, но и также сам компонент:
export * from './lib/posts-last.component';
export * from './lib/posts-last.module';
Возможно это поправят, правда непонятно когда.
Навигация в remote приложениях
Еще один неприятный момент связан с навигацией приложения, которое использует относительные пути:
/posts
/post/:id
В данном случае, если запустить Angular приложение, то при обращении к станицам /posts
и post/my-random-id
приложение отобразит нужные компоненты.
Однако, если приложение будет использоваться в качестве remote
, то в host
приложении для remote приложения будет некий префикс. Например, в данном случае в shell
приложении для реализации сайта новостей с использованием ngrx
будет префикс /ngrx
.
Тогда навигация будет следующей:
/ngrx/posts
/ngrx/post/:id
Однако, remote
приложение ничего не знает о префиксе. И тогда ссылки на страницы начинает ломаться.
Это можно решить если добавить InjectionToken
для префикса пути приложения. Тогда если remote
приложения будет запускаться в качестве самостоятельного приложения, то тогда префикс будет пустой строкой, а когда данное приложение будет использоваться в качестве remote, тогда префикс будет браться из передаваемого токена.
Установка токена будет выглядеть следующим образом:
export const PATH_REMOTE = new InjectionToken<string>('PATH_REMOTE');
@NgModule({
imports: [AppCoreModule, EntryRoutingModule],
providers: [
{
provide: PATH_REMOTE,
useValue: 'ngrx',
},
],
})
export class RemoteEntryModule {}
Единственный нюанс в том, что remote
должен дублировать роуты. То есть если приложение запускается как самостоятельное, то должны браться роуты из AppRouting
. А если приложение запускается как remote
, то пути должны быть из RemoteEntryRoutingModule
.
Для того, чтобы пути не пересекались, в AppRouting
для remote
можно задать префикс, который не будет использоваться при remote
.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LayoutComponent } from '@angular-samples/ui/layout';
const routes: Routes = [
{
path: '',
component: LayoutComponent,
children: [
// Путь для AppModule
{
path: '',
loadChildren: () => import('@angular-samples/redux/ngrx/posts/pages').then((modules) => modules.PagesModule),
},
// Путь для Remote application
{
path: 'remote',
loadChildren: () => import('./remote-entry/entry.module').then((m) => m.RemoteEntryModule),
},
],
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
initialNavigation: 'enabledBlocking',
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}
Ссылки
Предыдущая статья — Введение.
Следующая статья — Создание базовых классов.
Все исходники находятся на github, в репозитории:
Для того, чтобы посмотреть состояние проекта на момент написания статьи, нужно выбрать соответствующий тег — redux.
Подписывайтесь на блог, чтобы не пропустить новые статьи про Angular, и веб-разработку. Medium | Telegram| VK |Tw| Ln