Вступ
У сфері веб-розробки, де інновації розвиваються завдяки постійному прагненню до масштабованості, підтримуваності та гнучкості, традиційні монолітні архітектури часто стикаються з труднощами в масштабних застосунках. Обмеження монолітної архітектури, яка характеризується тісно пов'язаними компонентами та єдиною, монолітною кодовою базою, створюють значні проблеми, коли додатки зростають у складності та базі користувачів. Поки розробники борються з обмеженнями цього підходу, парадигмальне переключення на мікрофронтенд-архітектуру стає переконливим рішенням.
Мікрофронтенд-архітектура, натхненна успіхом мікросервісів на бекенді, пропонує революційний підхід до фронтенд-розробки. Розбиваючи монолітні фронтенд-додатки на менші, автономні одиниці, відомі як мікрофронтенди, цей архітектурний стиль відкриває шлях для подолання обмежень монолітної архітектури.
Кожен мікрофронтенд працює незалежно, зосереджуючись на конкретній функції або можливості, але при цьому безшовно інтегрується з іншими мікрофронтендами, утворюючи єдиний користувацький досвід. Такий модульний і децентралізований підхід не тільки покращує масштабованість і підтримуваність, але й сприяє культурі інновацій, даючи можливість командам розробників швидко вносити зміни і доставляти користувачам цінність з більшою ефективністю. Під час нашого вивчення мікрофронтенд-архітектури з використанням React.js ми розкриємо принципи, техніки та практичні поради, що лежать в основі цього трансформаційного підходу до фронтенд-розробки.
Проблеми з монолітною архітектурою
Монолітна архітектура, колись основа веб-розробки, тепер стає джерелом численних труднощів в епоху сучасних складних додатків. Хоча вона підходить для менших проєктів, вроджені обмеження монолітної архітектури стають все більш вираженими в міру масштабування та еволюції додатків.
Давайте детально розглянемо деякі з основних проблем, що виникають при використанні монолітної архітектури.
- Проблеми зі масштабованістю. Монолітні додатки часто стикаються з обмеженнями масштабованості через свою тісно зв'язану природу. У міру того як додаток зростає, масштабування стає складним завданням, що вимагає масштабування або дублювання всього моноліту. Такий підхід не тільки збільшує споживання ресурсів, але й створює проблеми в управлінні залежностями та забезпеченні узгодженості між масштабованими інстанціями.
- Обмежена швидкість розробки. У монолітних архітектурах розробники обмежені єдиною кодовою базою, що часто призводить до зниження швидкості розробки. Зміни або оновлення однієї частини додатку можуть випадково вплинути на інші компоненти, що вимагає ретельного тестування та координації. Це тісне з'єднання обмежує гнучкість і інноваційність, ускладнюючи введення нових функцій або швидке вдосконалення.
3.
## Складність і навантаження на підтримку. Зі збільшенням розміру та масштабів монолітних додатків, вони стають все більш складними та важкими для підтримки. Взаємозалежності між різними компонентами ускладнюють ізоляцію проблем або налагодження помилок, що призводить до довших циклів підтримки та зростання технічного боргу. Більше того, монолітна кодова база часто не має чітко визначених меж, що ускладнює ефективну співпрацю команд і розуміння загальної архітектури системи.
Уявіть собі гіпотетичну платформу електронної комерції, побудовану на монолітній архітектурі. Як тільки платформа набирає популярності, команда розробників стикається з проблемами масштабованості під час пікових навантажень, наприклад, під час святкових розпродажів. Через тісну прив'язку компонентів фронтенду та бекенду в монолітній архітектурі, масштабування стає складним завданням, що вимагає додаткових ресурсів для розподілу по всій стека додатка.
Це не тільки збільшує витрати на інфраструктуру, але й вводить складнощі у підтримку консистентності та забезпечення безперебійного користувацького досвіду. Крім того, коли команда намагається впровадити нову функцію оформлення замовлення для покращення користувацького досвіду, вони стикаються з труднощами через взаємозв'язану природу монолітної кодової бази. Зміни, внесені в модуль оформлення замовлення, ненавмисно впливають на інші частини додатка, що призводить до подовження циклів розробки та затримок у випуску нових функцій. Як наслідок, команда не встигає за змінюваними вимогами користувачів, що підкреслює обмеження монолітної архітектури у забезпеченні масштабованості та гнучкості в умовах швидких змін.
Вступ до технік мікрофронтендів
Оскільки розробники намагаються подолати обмеження монолітної архітектури і прийняти більш гнучкий, масштабований підхід до фронтенд-розробки, техніки мікрофронтендів стали маяком інновацій.
Ці техніки, натхненні принципами мікросервісів на бекенді, пропагують розбиття монолітних фронтенд-додатків на менші, автономні одиниці, відомі як мікрофронтенди. Давайте розглянемо деякі з основних технік мікрофронтендів, які дозволяють розробникам створювати модульні, підтримувані та масштабовані фронтенд-архітектури.
01. Магазин активів. Підхід Магазину активів передбачає відокремлення фронтенд-компонентів та активів, таких як JavaScript бандли, CSS файли та зображення, від основного додатку. Виводячи ці активи в центральне сховище або CDN (мережа доставки контенту), розробники можуть ділитися та повторно використовувати їх у кількох додатках або мікрофронтендах. Цей підхід сприяє повторному використанню коду, спрощує обслуговування та покращує продуктивність, використовуючи механізми кешування браузера.
02.
Модульна Федерація. Модульна Федерація, потужна техніка, представлена в webpack 5, дозволяє динамічно завантажувати та ділитися модулями JavaScript між мікрофронтендами під час виконання. Цей підхід дозволяє розробникам створювати незалежно розгортаються мікрофронтенди, які можуть безперешкодно інтегруватися. Динамічне імпортування віддалених модулів за допомогою Модульної Федерації сприяє модульній архітектурі, де мікрофронтенди можуть розвиватися і масштабуватися незалежно, без втрати сумісності.
03. iFrames і Web Components. Хоча iFrames і Web Components не є строго мікрофронтендними техніками, вони пропонують альтернативні підходи до досягнення модульності фронтенду та інкапсуляції. iFrames дозволяють розробникам вбудовувати ізольовані HTML-документи в батьківський документ, що дозволяє незалежне рендеринг та виконання. Web Components, з іншого боку, надають стандартизований спосіб інкапсуляції та повторного використання компонентів UI в різних веб-додатках.
Хоча ці підходи мають свої переваги та обмеження, вони можуть бути корисними інструментами в певних ситуаціях.
Глибоке занурення в Модульну Федерацію
Серед безлічі технік мікрофронтендів Модульна Федерація виділяється як справжня революція, пропонуючи безпрецедентну гнучкість і взаємодію при створенні модульних фронтенд-архітектур. Введена в webpack 5, Модульна Федерація переосмислює фронтенд-розробку, дозволяючи динамічно завантажувати та ділитися модулями JavaScript між мікрофронтендами під час виконання. Давайте глибше зануримось у Модульну Федерацію, розкриваючи її складнощі та досліджуючи її трансформуючий потенціал у сучасній веб-розробці.
Розуміння Модульної Федерації
В основі Модульної Федерації лежить можливість мікрофронтендів динамічно імпортувати та ділитися модулями JavaScript через межі додатків.
На відміну від традиційних статичних імпортів, Модульна Федерація дозволяє модулям завантажуватись асинхронно під час виконання, що забезпечує безшовну інтеграцію між мікрофронтендами. Ця здатність до динамічного завантаження модулів дає розробникам можливість створювати незалежно розгортаються мікрофронтенди, які можуть взаємодіяти і співпрацювати, сприяючи створенню модульної та компонуємої фронтенд-архітектури.
Ключові концепції та переваги
- Децентралізована архітектура - Модульна Федерація сприяє децентралізованій архітектурі, де кожен мікрофронтенд відповідає за власний набір функцій і залежностей. Така децентралізація дозволяє командам працювати автономно, незалежно розгортати та масштабувати свої мікрофронтенди, не впливаючи на інші частини додатку.
-
Динамічне завантаження модулів - Однією з характерних особливостей Модульної Федерації є підтримка динамічного завантаження модулів.
Динамічно імпортуючи віддалені модулі під час виконання, мікрофронтенди можуть отримувати та інтегрувати функціональність за запитом, зменшуючи початковий час завантаження та покращуючи продуктивність. -
Спільні залежності - Модульна Федерація сприяє спільному використанню залежностей між мікрофронтендами, зменшуючи дублювання та оптимізуючи використання ресурсів. Спільні залежності завантажуються один раз і кешуються серед мікрофронтендів, забезпечуючи консистентність і знижуючи накладні витрати.
Реалізація мікрофронтенд-додатку з використанням Модульної Федерації
Тепер, коли ми краще розуміємо Модульну Федерацію та її трансформаційний потенціал, час взятися за практичні аспекти реалізації мікрофронтенд-додатку з використанням цієї революційної техніки. У цьому розділі ми крок за кроком розглянемо створення зразка мікрофронтенд-додатку з використанням Модульної Федерації в React.js.
Від налаштування конфігурації webpack до динамічного імпорту та обміну модулями між мікрофронтендами — ми охопимо все, що потрібно знати, щоб розпочати вашу подорож у світ мікрофронтендів.
Щоб почати наш проект мікрофронтенду, ми використаємо pnpx (частина пакувальника pnpm) для створення нового додатку. Відкрийте термінал і виконайте наступну команду, щоб встановити pnpm, якщо у вас його ще немає.
npm i -g pnpm
Тепер ви можете створити додаток з Модульною Федерацією, використовуючи pnpm. Відкрийте термінал і виконайте наступну команду. Це створить каркас для вашого мікрофронтенд-додатку, а також надасть необхідні імена, фреймворки та порти для початку створення вашого шаблону.
pnpx create-mf-app
Далі давайте створимо Header.jsx та Footer.jsx для нашого додатку. Після того, як ми створимо Header і Footer, ми зможемо імпортувати їх до App.jsx та використовувати.
Тепер давайте додамо наші header та footer до додатку Products. Почнемо магію. Спочатку нам потрібно відредагувати файл webpack.config.js. Нам потрібно експонувати наші Header та Footer з нашого Home-додатку. Після того, як ми їх експонуємо, потрібно перезапустити сервер Home.
Тепер ми можемо знайти наш файл remoteEntery.js за URL-адресою додатку Home. RemoteEntery.js — це маніфест всіх модулів, які експонуються додатком Home.
Ми повинні скопіювати цей URL і перенести його до додатку сторінки продуктів. Всередині файлу webpack.config.js ми знаходимо місце для вставлення наших remote URL-адрес. Вставляємо скопійовану URL-адресу в секцію remotes і перезапускаємо сервер.
Тепер у вас є доступ до всіх експонованих компонентів з додатку сторінки Home.
Тепер ми можемо використовувати header та footer.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import Header from "home/Header";
import Footer from "home/Footer";
const App = () => (
<div>
<Header />
<h1>Products</h1>
<p>Welcome to the Products page</p>
<Footer />
</div>
);
ReactDOM.render(<App />, document.getElementById("app"));
Вітаємо, ваш micro-frontend додаток тепер працює ідеально.
Тепер давайте розглянемо деякі хороші практики, які ми маємо використовувати в архітектурі micro-frontend.
Асинхронне завантаження
Асинхронне завантаження використовується в функціях import() в середині функції lazy(), щоб дозволити динамічне та не блокуюче завантаження модулів під час виконання. Використовуючи асинхронне завантаження, браузер може продовжувати рендеринг основного інтерфейсу програми, не чекаючи, поки віддалені модулі будуть завантажені. Такий підхід покращує досвід користувача, зменшуючи час початкового завантаження та покращуючи продуктивність, особливо в ситуаціях, коли програма залежить від зовнішніх ресурсів або micro frontends.
Як результат, користувачі можуть швидше взаємодіяти з додатком, одночасно отримуючи переваги від модульної та масштабованої архітектури, що забезпечується за допомогою технік micro frontend, таких як Module Federation.
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import "./index.css";
const Header = React.lazy(() => import("home/Header"));
const Footer = React.lazy(() => import("home/Footer"));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<div>Products</div>
<div>Welcome to the Products page</div>
</Suspense>
);
ReactDOM.render(<App />, document.getElementById("app"));
Компонент
У наведеному прикладі, коли функція lazy() ініціює асинхронне завантаження віддалених компонентів (Header та Footer), компонент
Якщо віддалені компоненти успішно завантажено, компонент
Обробка помилок
У контексті архітектури мікрофронтендів, де незалежно розгорнуті мікрофронтенди співпрацюють для створення єдиного користувацького досвіду, наявність надійних механізмів обробки помилок є важливою для забезпечення стійкості та надійності додатку. Ось де вступає SafeComponent — шаблон, який надає систематичний підхід до коректної обробки помилок у мікрофронтенд-додатках. На відміну від традиційних методів обробки помилок, які можуть призводити до каскадних збоїв або різких зупинок інтерфейсу, SafeComponent пропонує проактивне рішення для виявлення і пом'якшення помилок, тим самим захищаючи користувацький досвід.
Давайте розглянемо, як працює SafeComponent і яке його значення в контексті архітектури мікрофронтендів.
SafeComponent
SafeComponent — це шаблон проектування, який інкапсулює компоненти або модулі, схильні до помилок, у захисну оболонку, що дозволяє контролювати поширення помилок і забезпечувати м'яке зниження функціональності в разі помилок. Ізолюючи код, який може спричинити помилки, в межах SafeComponent, розробники можуть запобігти поширенню помилок на компоненти вищого рівня або порушенню роботи всього додатку. Крім того, SafeComponent надає механізми для виявлення помилок, ведення журналів і відновлення, що дає змогу розробникам проактивно реагувати на помилки та підтримувати стабільність додатку.
У React.js SafeComponent можна реалізувати за допомогою меж помилок (error boundaries) — спеціальних компонентів, які ловлять JavaScript-помилки в будь-якому місці їхнього дерева дочірніх компонентів і замінюють їх на резервний інтерфейс користувача.
Давайте подивимося, як ми можемо створити обгортку SafeComponent у React.js.
import React, { Component } from 'react';
class SafeComponent extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Логування помилок у сервіс моніторингу помилок
console.error('Помилка, зафіксована SafeComponent:', error, errorInfo);
// Додаткове оброблення помилок (наприклад, сповіщення користувача, м'яке відновлення)
// За бажанням, оновити стан для тригеру оновлення інтерфейсу користувача
}
render() {
if (this.state.hasError) {
// Відображення резервного інтерфейсу
return \Щось пішло не так.
}
Будь ласка, спробуйте ще раз пізніше.\;
}
// Відображення дочірніх компонентів
return this.props.children;
}
}
export default SafeComponent;
Тепер ми можемо обгорнути наші віддалені компоненти в SafeComponent.
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import "./index.css";
import SafeComponent from "./SafeComponent";
const Header = React.lazy(() => import("home/Header"));
const Footer = React.lazy(() => import("home/Footer"));
const App = () => (
\
\Loading...\} >
\
\Products\ \Welcome to the Products page\
\
);
ReactDOM.render(\, document.getElementById("app"));
Тепер наш додаток безпечний.
Якщо в *\
* віддаленому компоненті виникає помилка, наша сторінка все одно працює, не зриваючись.
Спільне використання функцій та станів
Спільне використання функцій
Спільне використання функцій між мікрофронтендами дозволяє повторно використовувати код та сприяє модульному підходу до розробки фронтенду. Ось приклад того, як поділитися утилітною функцією між кількома мікрофронтендами. Створіть файл utils.js у директорії src вашого додатку Home.
Потім потрібно експортувати це з webpack.config.js.
export function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
exposes: {
"./Header": "./src/Header.jsx",
"./Footer": "./src/Footer.jsx",
"./utils": "./src/utils.js",
},
Тепер ви можете використовувати ваші віддалені утиліти з додатку Products.
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import "./index.css";
import SafeComponent from "./SafeComponent";
import { capitalize } from 'home/utils';
const Header = React.lazy(() => import("home/Header"));
const Footer = React.lazy(() => import("home/Footer"));
const App = () => (
<SafeComponent>
<Suspense fallback={<div>Loading...</div>}>
<Header />
<div> { capitalize("products") } </div>
<Footer />
</Suspense>
</SafeComponent>
);
ReactDOM.render(<App />, document.getElementById("app"));
Спільне використання станів
Спільне використання станів між мікрофронтендами сприяє синхронізованому управлінню даними та дозволяє компонентам ефективно взаємодіяти і координувати свою роботу.
Ось приклад, який демонструє, як поділитися станом за допомогою спільного контексту в React.js. Створіть файл SharedContext.js у директорії src вашого додатку Home.
Потім ви повинні експонувати його в webpack.config.js.
import React, { createContext, useContext, useState } from 'react';
const SharedContext = createContext();
export function useSharedContext() {
return useContext(SharedContext);
}
export function SharedProvider({ children }) {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(prevCount => prevCount + 1);
};
return (
<SharedContext.Provider value={{ count, incrementCount }}>
{children}
</SharedContext.Provider>
);
}
exposes: {
"./Header": "./src/Header.jsx",
"./Footer": "./src/Footer.jsx",
"./utils": "./src/utils.js",
'./SharedContext': './src/SharedContext.js',
},
У цьому прикладі ми визначаємо спільний контекст (SharedContext) і компонент провайдера (SharedProvider), щоб управляти спільним станом (count).
Ми також визначаємо власний хук (useSharedContext), щоб отримати доступ до спільного стану та функцій.
Тепер ви можете використовувати додаток лічильника у своїх мікрофронтенд додатках.
Я інтегрував цей лічильник у додаток сторінки Продуктів.
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import "./index.css";
import SafeComponent from "./SafeComponent";
import { useSharedContext, SharedProvider } from 'home/SharedContext';
import { capitalize } from 'home/utils';
const Header = React.lazy(() => import("home/Header"));
const Footer = React.lazy(() => import("home/Footer"));
function SomeComponent() {
const { count, incrementCount } = useSharedContext();
return (
<div>
<div>Count: {count}</div>
<button onClick={incrementCount}>Increment count</button>
</div>
);
}
const App = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<div>
{ }
<h1>{capitalize("products")}</h1>
<p>Welcome to the Products page</p>
</div>
</Suspense>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
Щоб використовувати цей лічильник, я створив ще один додаток з використанням Module Federation, який називається сторінка Кошика.
і використовуйте лічильник на сторінці кошика.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import { useSharedContext, SharedProvider } from 'home/SharedContext';
function ComponentCount() {
const { count } = useSharedContext();
console.log('count:', count)
return <div>Count: {count}</div>;
}
const App = () => (
<div>
<h1>Name: cart</h1>
</div>
);
ReactDOM.render(<App />, document.getElementById("app"));
Висновок
У сфері фронтенд-розробки архітектура мікрофронтендів є символом інновацій, пропонуючи модульний, масштабований і колаборативний підхід до створення сучасних веб-додатків.
Розбиваючи монолітні фронтенд-додатки на менші, незалежно розгортаються мікрофронтенди, розробники можуть подолати обмеження традиційних архітектур і відкрити нові можливості для інновацій та гнучкості. Протягом цього шляху ми вивчали основні принципи та техніки архітектури мікрофронтендів, від Module Federation і асинхронного завантаження до обробки помилок за допомогою SafeComponent та обміну функціями й станами.
Озброєні цими знаннями та практичними прикладами, ви готові вирушити в подорож по мікрофронтендам, революціонізуючи підходи до концептуалізації, створення та еволюції фронтенд-додатків у цифрову епоху.
Досліджуйте повну кодову базу та зануртесь глибше у демонстрацію архітектури мікрофронтендів з React.js за допомогою Module Federation та інших технік у моєму GitHub репозиторії.
Якщо ця стаття була корисною або пізнавальною, подумайте про підтримку моїх зусиль, купивши мені каву. Разом давайте формувати майбутнє фронтенд-розробки та створювати чудові враження для користувачів по всьому світу. Успіхів у кодингу!
Побачимось!
Перекладено з: A Deep Dive into Micro Frontend Architecture with React.js