
Сьогодні розглянемо, як організувати взаємодію мікрофронтенду (компонентний зв'язок) із RxJ. Ми також застосуємо стратегію кодового доступу, як-от підмодулі Git, щоб зберегти єдине джерело та легко ділитися основною кодовою базою між застосунками. Робитимемо все максимально просто, щоб зосередитись більше на базових концепціях та практиці.
Задум нашого проєкту — створити застосунок Todo, у якому користувачі вводитимуть текст, натискатимуть «Додати» і цей пункт з'являтиметься у списку завдань. Користувачі також зможуть видаляти елементи кнопкою «Закрити».
Загалом ми розглянемо чотири проєкти:
-
Todo-Core (TypeScript);
-
Todo (Single-Spa);
-
Todo-Form (Angular);
-
Todo-List (React).


Todo-Core (підмодулі Git)
Ключовий модуль застосовуватиметься у всіх застосунках і матиме всю основну логіку із загальними моделями та службами. Крім того, він буде використовуватися як підмодуль Git.
Примітка: Замість підмодулів Git можна користуватися пакунками node як стратегією створення спільного коду, це може бути навіть простіше.
1. Створіть репозиторій з назвою todo-core
(може містити README.md
).
2. Клонуйте свій репозиторій:
git clone http://github.com/your_username/todo-core.git
3. Перейдіть до проєкту:
cd todo-core
4. Ініціалізуйте проєкт Node:
npm init --yes
5. Установіть бібліотеку rxjs
:
npm install rxjs
6. Ініціалізуйте проєкт TypeScript:
tsc --init
7. Оновіть tsconfig.json:
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
8. Додайте .gitignore:
node_modules
9. Додайте src/base.service.ts:
import { BehaviorSubject } from "rxjs";
export abstract class BaseService {
abstract list$: BehaviorSubject<string[]>;
abstract addTodo(todo: string): void;
abstract removeTodo(index: number): void;
}
10. Додайте src/base.service.impl.ts:
import { BehaviorSubject } from "rxjs";
import { BaseService } from "./base.service";
export const BaseServiceImpl: BaseService = Object.freeze({
list$: new BehaviorSubject<string[]>([]),
addTodo(todo: string): void {
this.list$.next([...this.list$.getValue(), todo]);
},
removeTodo(todoIndex: number): void {
const updatedList = this.list$
.getValue()
.filter((el, index) => index !== todoIndex);
\t\t\t
\t\t\tthis.list$.next(updatedList);
},
});
11. Додайте src/index.ts:
export * from "./base.service";
export * from "./base.service.impl";
12. Закомітьте та надішліть зміни до віддаленого репозиторію:
git add .
git commit -m "Core project setup"
git push

Todo (макет застосунку Single-Spa)
Цей проєкт буде відповідати за структуру фреймворку та макет вмісту.
1. Створіть теку проєкту та перейдіть до неї:
mkdir todo && cd todo
2. Ініціалізуйте макет проєкту Single-Spa:
create-single-spa --layout
та виберіть:
-
Directory for new project —
.
-
Select type to generate —
single-spa root config
-
Which package manager do you want to use? —
npm
-
Will this project use TypeScript —
y
-
Organization name —
obaranovskyi
3. Встановіть залежності:
npm install
4. Відкрийте src/microfrontend-layout.html та вилучіть цей рядок:
<application name="@single-spa/welcome"></application>
5. Відкрийте src/index.ejs та вилучіть цей рядок:
"@single-spa/welcome": "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js",

Вбудовування підмодулів:
6. Додайте todo-core
підмодулем Git:
git submodule add https://github.com/your_user/todo-core.git
7. Додайте src/externals.ts:
export * from "../todo-core/src/index";
8. Оновіть src/obaranovskyi-root-config.ts:
import { BaseServiceImpl } from "./externals";
// ...
window["todoCore"] = BaseServiceImpl;
9. Запустіть проєкт:
npm start

Застосунок Todo-Form (Angular)
У цьому проєкті ми створимо todo-форму з кнопкою надсилання.
1. Створіть застосунок Angular за допомогою Angular CLI
ng new todo-form
та виберіть:
-
Do you want to enforce stricter type checking and stricter bundle budgets in the workspace? —
y
-
Would you like to add angular routing? —
n
-
Which stylesheet format would you like to use? —
SCSS
2. Перейдіть до проєкту й установіть залежності:
cd todo-form && npm install
3. Установіть Single-Spa:
ng add single-spa-angular
та виберіть:
-
Does your application use angular routing? —
n
-
Does your application use the browserAnimationModule —
n

Вбудовування підмодулів:
4. Додайте todo-core
підмодулем Git:
git submodule add https://github.com/your_user/todo-core.git
5. Додайте paths з мапінгом todo-core
до tsconfig.json:
{
"compilerOptions": {
...
"baseUrl": "./",
"paths": {
"@todo-core/*": ["./todo-core/src/*"]
}
},
...
}
Примітка: baseUrl
обов'язково має бути на своєму місці.
6. Оновіть src/app/app.module.ts:
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BaseService } from '@todo-core/base.service';
import { AppComponent } from './app.component';
import { TodoFormComponent } from './todo-form/todo-form.component';
@NgModule({
declarations: [AppComponent, TodoFormComponent],
imports: [BrowserModule, FormsModule],
providers: [{ provide: BaseService, useValue: (window as any).todoCore }],
bootstrap: [AppComponent],
})
export class AppModule {}
7. Оновіть src/app/app.component.html:
<app-todo-form></app-todo-form>
Реалізація компонента Todo-форми
8. Згенеруйте компонент todo-form
:
ng generate component todo-form
9. Оновіть src/app/todo-form/todo-form.component.html:
<div class="todo-form">
<div class="todo-form-container">
<input type="text" [(ngModel)]="todo" placeholder="Enter text
..." class="todo-form-input">
<button (click)="addTodo()" class="todo-form-submit">Add</button>
</div>
</div>
10. Оновіть src/app/todo-form/todo-form.component.ts:
import { Component } from '@angular/core';
import { BaseService } from '@todo-core/base.service';
@Component({
selector: 'app-todo-form',
templateUrl: './todo-form.component.html',
styleUrls: ['./todo-form.component.scss'],
})
export class TodoFormComponent {
todo!: string;
constructor(private readonly baseService: BaseService) {}
addTodo(): void {
if (!this.todo) {
return;
}
this.baseService.addTodo(this.todo);
this.todo = '';
}
}
11. Оновіть src/app/todo-form/todo-form.component.scss:
.todo-form {
position: relative;
left: calc(50% - 250px);
top: 200px;
\t\t
\t\t&-input {
width: 500px;
padding: 11px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
\t\t
\t\t&-submit {
background-color: crimson;
border: none;
color: white;
padding: 10px 40px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin-left: 15px;
}
}
12. Установіть залежності:
npm install
13. Запустіть проєкт:
npm run serve:single-spa:todo-form

Оновлення проєкту Todo:
14. Включіть бібліотеку zone.js до src/index.ejs:
<script src="https://unpkg.com/zone.js"></script>
15. Оновіть імпорт асоціативного масиву (map) SystemJS у src/index.ejs:
<script type="systemjs-importmap">
{
"imports": {
"@obaranovskyi/root-config": "//localhost:9000/obaranovskyi-root-config.js",
"@obaranovskyi/todo-form": "//localhost:4200/main.js"
}
}
</script>
16. Зареєструйте застосунок у src/obaranovskyi-root-config.ts:
registerApplication(
"@obaranovskyi/todo-form",
() => System.import("@obaranovskyi/todo-form"),
(location) => true
);

Todo List (React)
У цьому проєкті ми створимо список завдань із функціональністю вилучення елементів.
1. Створіть теку проєкту та перейдіть до неї:
mkdir todo-list && cd todo-list
2. Встановіть проєкт Single-Spa/React:
create-single-spa --framework react
та виберіть:
-
Directory for new project —
.
-
Which package manager do you want to use? —
npm
-
Will this project use TypeScript —
y
-
Organization name —
obaranovskyi
-
Project name —
todo-list
3. Встановіть залежності:
npm install
4. Встановіть бібліотеку rxjs
:
npm install rxjs

Вбудовування підмодулів:
5. Додайте todo-core
підмодулем Git:
git submodule add https://github.com/your_user/todo-core.git
6. Додайте src/externals.ts:
import { BaseService } from "../todo-core/src/base.service";
export const baseService: BaseService = (window as any).todoCore as BaseService;
export * from "../todo-core/src/index";
7. Додайте src/TodoList.tsx:
import React from "react";
import { Subject, takeUntil } from "rxjs";
import { baseService } from "./externals";
import "./TodoList.css";
export interface IProps {}
export interface IState {
todos: string[];
}
export class TodoList extends React.Component<IProps, IState> {
destroy$: Subject<void> = new Subject<void>();
constructor(props) {
super(props);
this.state = { todos: [] };
}
\t
\tcomponentDidMount(): void {
this.observeTodos();
}
\t
\tobserveTodos(): void {
baseService.list$
.pipe(takeUntil(this.destroy$))
.subscribe((list: string[]) => {
this.setState({ todos: list });
});
}
\t
\tcomponentWillUnmount(): void {
this.destroy$.next();
this.destroy$.complete();
}
\t
\tremoveTodo = (index: number) => {
baseService.removeTodo(index);
};
\t
\trender() {
return (
<div className="main">
<div className="container">
<div className="total">Total: {this.state.todos.length}</div>
<div className="list">
<ol>
{this.state.todos.map((todo: string, index: number) => (
<li key={index}>
{todo}{" "}
<span
role="button"
tabIndex={0}
className="close"
onClick={() => this.removeTodo(index)}
>
[x]
</span>
</li>
))}
</ol>
</div>
</div>
</div>
);
}
}
8. Додайте src/TodoList.css:
.main {
font-family: sans-serif;
position: relative;
top: 200px;
left: calc(50% - 250px);
}
.container {
max-width: 1000px;
}
.total {
text-decoration: underline;
}
.close {
cursor: pointer;
color: red;
}
9. Оновіть root.component.tsx:
import { TodoList } from "./TodoList";
export default function Root(props) {
return <TodoList />;
}
10. Запустіть проєкт:
npm start -- --port 8500

Todo project updates:
11. Оновіть src/index.ejs:
<script type="systemjs-importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js",
"@obaranovskyi/todo-list": "//localhost:8500/obaranovskyi-todo-list.js",
"@obaranovskyi/root-config": "//localhost:9000/obaranovskyi-root-config.js",
"@obaranovskyi/todo-form": "//localhost:4200/main.js"
}
}
</script>
12. Зареєструйте застосунок у src/obaranovskyi-root-config.ts:
registerApplication(
"@obaranovskyi/todo-list",
() => System.import("@obaranovskyi/todo-list"),
(location) => true
);
Тепер усе повинно працювати чудово! :)
Ще немає коментарів