Redux в Angular. Генерация приложения с микрофронтендами.

Aleksandr Serenko
F.A.F.N.U.R
Published in
4 min readNov 24, 2022

В данной статье описывается процесс создания приложения с микрофронтендами.

Для создания демо приложения будем использовать Nx.

Так как подразумевается использование 3 разных state management’ов, то для простоты демонстрации используем микрофронтенды.

Nx из коробки умеет создавать подобного рода без дополнительных телодвижений, так что процесс аналогичен стандартным использованием angular-cli.

Для работы будем использовать уже ранее созданный nx workspace с предустановленным Angularangular-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:4200redux-dashboard, основное shell приложение;
  • localhost:4201redux-ngrx, remote приложение c Ngrx;
  • localhost:4202redux-ngxs, remote приложение c Ngxs;
  • localhost:4203redux-akita, remote приложение c Akita.

Более подробно с использованием и настройкой микрофронтендов можно ознакомиться в документации.

Демо готового приложения — 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

--

--

Aleksandr Serenko
F.A.F.N.U.R

Senior Front-end Developer, Angular evangelist, Nx apologist, NodeJS warlock