🧩 Что такое Micro Frontends (Микрофронтенды)?
Простое объяснение: Представьте, что вместо одного огромного веб-сайта у вас есть несколько маленьких сайтов-лего, которые собираются вместе в браузере:
- Один блок — меню навигации
- Второй — список товаров
- Третий — корзина покупок
- Четвёртый — рекомендации от ИИ
Каждый блок — это отдельная программа, которую можно:
- Разрабатывать независимо
- Обновлять отдельно
- Писать на разных технологиях
- Запускать разными командами
🚀 Шаг 1: Подготовка среды разработки
Цель шага: Установить всё необходимое для создания микрофронтендов.
Что нужно установить:
- Node.js и npm — как "двигатель" для JavaScript программ
- Visual Studio Code — редактор кода (или любой другой)
- Git — для контроля версий
Пошаговая установка:
bash
# 1. Проверяем, что Node.js установлен
node --version # Должна быть версия 16 или выше
# 2. Проверяем npm
npm --version
# 3. Если не установлено, скачиваем с https://nodejs.org/
# Выбираем LTS версию (рекомендованная)
# 4. Устанавливаем Vite - современный инструмент для сборки
npm create vite@latest
Создаём структуру проекта:
text
my-microfrontend-project/
├── container/ # Главное приложение-контейнер
├── header-app/ # Микрофронтенд: шапка
├── dashboard-app/ # Микрофронтенд: дашборд ИИ
├── chat-app/ # Микрофронтенд: чат с ИИ
└── shared/ # Общие ресурсы
Что должно получиться:
✅ Node.js и npm установлены
✅ Можем создавать новые проекты
✅ Готова структура папок
На что обратить внимание:
- Node.js версия должна быть актуальной
- Vite работает быстрее, чем старый create-react-app
- Можно использовать любую папку для проекта
📝 Шаг 2: Создаём первый микрофронтенд - "Шапка"
Цель шага: Создать независимый компонент, который будет работать отдельно.
Что такое "компонент":
Это как LEGO-деталька. У неё есть:
- Внешний вид (HTML/CSS)
- Логика (JavaScript)
- Возможность принимать параметры
- Возможность генерировать события
Создаём приложение шапки:
bash
# Переходим в папку проекта
cd my-microfrontend-project
# Создаём приложение шапки с помощью Vite + React
npm create vite@latest header-app -- --template react
cd header-app
npm install
Смотрим структуру созданного приложения:
text
header-app/
├── public/ # Статические файлы
├── src/
│ ├── App.jsx # Главный компонент
│ ├── main.jsx # Точка входа
│ └── App.css # Стили
├── index.html # Основной HTML
├── package.json # Настройки проекта
└── vite.config.js # Конфигурация сборки
Редактируем компонент шапки:
jsx
// header-app/src/App.jsx
import './App.css';
// Компонент — это функция, которая возвращает JSX (HTML + JavaScript)
function App() {
return (
<div className="header-app">
<header className="app-header">
{/* Логотип */}
<div className="logo">
<img src="/vite.svg" alt="Лого" />
<span>ИИ-Ассистент 2024</span>
</div>
{/* Навигация */}
<nav className="navigation">
<button className="nav-btn active">
<span>🏠</span> Главная
</button>
<button className="nav-btn">
<span>🤖</span> Агенты
</button>
<button className="nav-btn">
<span>📊</span> Аналитика
</button>
<button className="nav-btn">
<span>⚙️</span> Настройки
</button>
</nav>
{/* Пользователь */}
<div className="user-info">
<div className="avatar">АИ</div>
<span className="username">ИИ-Разработчик</span>
<button className="logout-btn">🚪</button>
</div>
</header>
</div>
);
}
export default App;
Добавляем стили:
css
/* header-app/src/App.css */
.header-app {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
border-bottom: 2px solid #4a90e2;
}
.app-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.logo {
display: flex;
align-items: center;
gap: 1rem;
font-size: 1.5rem;
font-weight: bold;
}
.logo img {
width: 40px;
height: 40px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.navigation {
display: flex;
gap: 1rem;
}
.nav-btn {
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
}
.nav-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
.nav-btn.active {
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.user-info {
display: flex;
align-items: center;
gap: 1rem;
}
.avatar {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.logout-btn {
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
transition: all 0.3s ease;
}
.logout-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: rotate(90deg);
}
Запускаем приложение отдельно:
bash
# В папке header-app
npm run dev
Открываем в браузере: http://localhost:5173
Что должно получиться:
✅ Красивая шапка с логотипом, навигацией и пользователем
✅ Анимации и hover-эффекты
✅ Можно кликать по кнопкам
✅ Приложение работает на отдельном порту
На что обратить внимание:
- Каждый микрофронтенд запускается на своём порту
- Можно разрабатывать независимо от других частей
- Стили изолированы в этом компоненте
- В реальном проекте нужна навигация между микрофронтендами
📊 Шаг 3: Создаём ИИ-дашборд как микрофронтенд
Цель шага: Создать сложный компонент с ИИ-логикой, который можно переиспользовать.
Что такое "дашборд":
Это панель управления с графиками, статистикой и кнопками. В нашем случае — панель управления ИИ-агентами.
Создаём приложение дашборда:
bash
# Возвращаемся в папку проекта
cd ..
npm create vite@latest dashboard-app -- --template react
cd dashboard-app
npm install
# Устанавливаем библиотеки для графиков
npm install recharts axios
Создаём дашборд с ИИ-метриками:
jsx
// dashboard-app/src/App.jsx
import { useState, useEffect } from 'react';
import {
LineChart, Line, BarChart, Bar,
PieChart, Pie, Cell,
XAxis, YAxis, CartesianGrid,
Tooltip, Legend, ResponsiveContainer
} from 'recharts';
import './App.css';
// ИИ-агенты (имитация данных)
const initialAgents = [
{ id: 1, name: 'Аналитик данных', status: 'active', accuracy: 94, cpu: 45 },
{ id: 2, name: 'Чат-бот поддержки', status: 'active', accuracy: 87, cpu: 32 },
{ id: 3, name: 'Прогноз продаж', status: 'warning', accuracy: 76, cpu: 68 },
{ id: 4, name: 'Классификатор изображений', status: 'inactive', accuracy: 92, cpu: 12 },
];
function Dashboard() {
// Состояние — переменные, которые могут меняться
const [agents, setAgents] = useState(initialAgents);
const [selectedAgent, setSelectedAgent] = useState(null);
// Данные для графиков
const performanceData = [
{ day: 'Пн', accuracy: 88, response: 120 },
{ day: 'Вт', accuracy: 92, response: 110 },
{ day: 'Ср', accuracy: 85, response: 130 },
{ day: 'Чт', accuracy: 94, response: 105 },
{ day: 'Пт', accuracy: 90, response: 115 },
{ day: 'Сб', accuracy: 87, response: 125 },
{ day: 'Вс', accuracy: 91, response: 118 },
];
const agentDistribution = [
{ name: 'Active', value: 2, color: '#4CAF50' },
{ name: 'Warning', value: 1, color: '#FFC107' },
{ name: 'Inactive', value: 1, color: '#F44336' },
];
// Имитация обновления данных каждые 5 секунд
useEffect(() => {
const interval = setInterval(() => {
setAgents(prev => prev.map(agent => ({
...agent,
cpu: Math.min(100, Math.max(0, agent.cpu + (Math.random() * 10 - 5))),
})));
}, 5000);
return () => clearInterval(interval);
}, []);
// Обработчик выбора агента
const handleAgentClick = (agent) => {
setSelectedAgent(agent);
};
// Обработчик запуска/остановки
const toggleAgentStatus = (id) => {
setAgents(prev => prev.map(agent =>
agent.id === id
? {
...agent,
status: agent.status === 'active' ? 'inactive' : 'active',
cpu: agent.status === 'active' ? 0 : 45
}
: agent
));
};
return (
<div className="dashboard-app">
<h1 className="dashboard-title">📊 Панель управления ИИ-агентами</h1>
<div className="dashboard-grid">
{/* Левая колонка: список агентов */}
<div className="agents-list">
<h2>🤖 Мои ИИ-агенты</h2>
{agents.map(agent => (
<div
key={agent.id}
className={`agent-card ${agent.status} ${selectedAgent?.id === agent.id ? 'selected' : ''}`}
onClick={() => handleAgentClick(agent)}
>
<div className="agent-info">
<h3>{agent.name}</h3>
<div className="agent-metrics">
<span className={`status-badge ${agent.status}`}>
{agent.status === 'active' ? '🟢 Активен' :
agent.status === 'warning' ? '🟡 Внимание' : '🔴 Неактивен'}
</span>
<span className="metric">Точность: {agent.accuracy}%</span>
<span className="metric">CPU: {agent.cpu.toFixed(1)}%</span>
</div>
</div>
<button
className={`toggle-btn ${agent.status === 'active' ? 'stop' : 'start'}`}
onClick={(e) => {
e.stopPropagation();
toggleAgentStatus(agent.id);
}}
>
{agent.status === 'active' ? '⏹️ Стоп' : '▶️ Запуск'}
</button>
</div>
))}
</div>
{/* Правая колонка: графики и детали */}
<div className="charts-section">
{/* График точности */}
<div className="chart-container">
<h3>📈 Точность работы за неделю</h3>
<ResponsiveContainer width="100%" height={200}>
<LineChart data={performanceData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="day" />
<YAxis />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="accuracy" stroke="#8884d8" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
</div>
{/* Распределение по статусам */}
<div className="chart-container">
<h3>🍩 Статусы агентов</h3>
<ResponsiveContainer width="100%" height={200}>
<PieChart>
<Pie
data={agentDistribution}
cx="50%"
cy="50%"
labelLine={false}
label={({ name, percent }) => `${name}: ${(percent * 100).toFixed(0)}%`}
outerRadius={80}
fill="#8884d8"
dataKey="value"
>
{agentDistribution.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
</div>
{/* Детали выбранного агента */}
{selectedAgent && (
<div className="agent-details">
<h3>🔍 Детали: {selectedAgent.name}</h3>
<div className="details-grid">
<div className="detail">
<span className="label">ID:</span>
<span className="value">{selectedAgent.id}</span>
</div>
<div className="detail">
<span className="label">Статус:</span>
<span className={`value status-${selectedAgent.status}`}>
{selectedAgent.status === 'active' ? 'Активен 🟢' :
selectedAgent.status === 'warning' ? 'Внимание 🟡' : 'Неактивен 🔴'}
</span>
</div>
<div className="detail">
<span className="label">Точность:</span>
<span className="value">{selectedAgent.accuracy}%</span>
</div>
<div className="detail">
<span className="label">Загрузка CPU:</span>
<div className="cpu-bar">
<div
className="cpu-fill"
style={{ width: `${selectedAgent.cpu}%` }}
></div>
<span>{selectedAgent.cpu.toFixed(1)}%</span>
</div>
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
}
export default Dashboard;
Добавляем стили дашборда:
css
/* dashboard-app/src/App.css */
.dashboard-app {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 2rem;
background: #f5f5f5;
min-height: 100vh;
}
.dashboard-title {
color: #333;
margin-bottom: 2rem;
font-size: 2rem;
display: flex;
align-items: center;
gap: 1rem;
}
.dashboard-grid {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 2rem;
height: calc(100vh - 120px);
}
/* Список агентов */
.agents-list {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
overflow-y: auto;
}
.agents-list h2 {
margin-bottom: 1.5rem;
color: #444;
}
.agent-card {
background: #f8f9fa;
border-radius: 10px;
padding: 1rem;
margin-bottom: 1rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
justify-content: space-between;
align-items: center;
border-left: 4px solid #ccc;
}
.agent-card:hover {
transform: translateX(5px);
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.agent-card.selected {
border-left-color: #4a90e2;
background: #e3f2fd;
}
.agent-card.active {
border-left-color: #4CAF50;
}
.agent-card.warning {
border-left-color: #FFC107;
}
.agent-card.inactive {
border-left-color: #F44336;
opacity: 0.7;
}
.agent-info h3 {
margin: 0 0 0.5rem 0;
color: #333;
}
.agent-metrics {
display: flex;
gap: 1rem;
font-size: 0.9rem;
}
.status-badge {
padding: 0.2rem 0.5rem;
border-radius: 12px;
font-size: 0.8rem;
}
.status-badge.active {
background: #e8f5e9;
color: #2e7d32;
}
.status-badge.warning {
background: #fff8e1;
color: #f57c00;
}
.status-badge.inactive {
background: #ffebee;
color: #c62828;
}
.metric {
color: #666;
}
.toggle-btn {
padding: 0.5rem 1rem;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
}
.toggle-btn.start {
background: #4CAF50;
color: white;
}
.toggle-btn.stop {
background: #F44336;
color: white;
}
.toggle-btn:hover {
opacity: 0.9;
transform: scale(1.05);
}
/* Графики */
.charts-section {
display: flex;
flex-direction: column;
gap: 2rem;
}
.chart-container {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.chart-container h3 {
margin-top: 0;
margin-bottom: 1rem;
color: #444;
}
/* Детали агента */
.agent-details {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.details-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin-top: 1rem;
}
.detail {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.label {
font-weight: bold;
color: #666;
font-size: 0.9rem;
}
.value {
font-size: 1.1rem;
color: #333;
}
.status-active {
color: #4CAF50;
}
.status-warning {
color: #FFC107;
}
.status-inactive {
color: #F44336;
}
.cpu-bar {
width: 100%;
height: 20px;
background: #e0e0e0;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.cpu-fill {
height: 100%;
background: linear-gradient(90deg, #4CAF50, #8BC34A);
transition: width 0.5s ease;
}
.cpu-bar span {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 0.8rem;
color: white;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
Запускаем дашборд отдельно:
bash
# В папке dashboard-app
npm run dev
Открываем в браузере: http://localhost:5174 (следующий порт)
Что должно получиться:
✅ Полноценный дашборд управления ИИ-агентами
✅ Интерактивные графики с анимациями
✅ Можно запускать/останавливать агентов
✅ Данные обновляются в реальном времени
✅ Адаптивная сетка (grid)
На что обратить внимание:
- Данные имитируются, в реальном проекте будут с API
- Графики используют библиотеку recharts
- Состояние управляется через React hooks (useState, useEffect)
- Стили полностью изолированы
💬 Шаг 4: Создаём ИИ-чат как микрофронтенд
Цель шага: Создать интерактивный компонент, который общается с бэкендом.
Что такое "чат с ИИ":
Компонент, где пользователь пишет сообщения, а ИИ-агент отвечает. Это отдельное приложение, которое можно встроить куда угодно.
Создаём приложение чата:
bash
# Возвращаемся в папку проекта
cd ..
npm create vite@latest chat-app -- --template react
cd chat-app
npm install
# Устанавливаем иконки и утилиты
npm install react-icons date-fns
Создаём чат-компонент:
jsx
// chat-app/src/App.jsx
import { useState, useRef, useEffect } from 'react';
import { FiSend, FiUser, FiBot, FiTrash2 } from 'react-icons/fi';
import { format } from 'date-fns';
import { ru } from 'date-fns/locale';
import './App.css';
// Типы сообщений
const MESSAGE_TYPES = {
USER: 'user',
BOT: 'bot',
SYSTEM: 'system'
};
// ИИ-агенты для выбора
const AI_AGENTS = [
{ id: 'general', name: '🤖 Общий ИИ-ассистент', description: 'Отвечает на общие вопросы' },
{ id: 'code', name: '💻 ИИ-разработчик', description: 'Помогает с кодом' },
{ id: 'analytics', name: '📊 Аналитик данных', description: 'Анализирует данные' },
{ id: 'creative', name: '🎨 Креативный ИИ', description: 'Генерирует идеи' },
];
function ChatApp() {
// Состояния
const [messages, setMessages] = useState([
{
id: 1,
type: MESSAGE_TYPES.BOT,
text: 'Привет! Я ваш ИИ-ассистент. Чем могу помочь?',
timestamp: new Date(),
agent: 'general'
}
]);
const [inputText, setInputText] = useState('');
const [selectedAgent, setSelectedAgent] = useState(AI_AGENTS[0]);
const [isTyping, setIsTyping] = useState(false);
// Референс для автоматической прокрутки
const messagesEndRef = useRef(null);
// Автопрокрутка при новых сообщениях
useEffect(() => {
scrollToBottom();
}, [messages]);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
// Отправка сообщения
const handleSendMessage = async () => {
if (!inputText.trim()) return;
// Добавляем сообщение пользователя
const userMessage = {
id: messages.length + 1,
type: MESSAGE_TYPES.USER,
text: inputText,
timestamp: new Date(),
agent: selectedAgent.id
};
setMessages(prev => [...prev, userMessage]);
setInputText('');
setIsTyping(true);
// Имитация ответа ИИ
setTimeout(() => {
const responses = {
general: [
'Интересный вопрос! Давайте разберемся...',
'Я понимаю ваш интерес. Вот что я могу сказать...',
'Хороший вопрос! На основе моих данных...',
'Позвольте мне подумать над этим...'
],
code: [
'Для решения этой задачи рекомендую использовать...',
'Вот пример кода на Python: ```python\nprint("Hello")\n```',
'Обратите внимание на архитектурный паттерн...',
'Для оптимизации производительности попробуйте...'
],
analytics: [
'Судя по данным, наблюдается тенденция...',
'Статистический анализ показывает...',
'Вот визуализация этих данных...',
'Корреляционный анализ указывает на...'
],
creative: [
'Как насчет такой идеи...',
'Представьте себе...',
'Что если мы попробуем...',
'Вдохновляясь этим, можно создать...'
]
};
const botResponse = {
id: messages.length + 2,
type: MESSAGE_TYPES.BOT,
text: responses[selectedAgent.id][Math.floor(Math.random() * responses[selectedAgent.id].length)],
timestamp: new Date(),
agent: selectedAgent.id
};
setMessages(prev => [...prev, botResponse]);
setIsTyping(false);
}, 1500 + Math.random() * 1000);
};
// Очистка истории
const clearChat = () => {
setMessages([
{
id: 1,
type: MESSAGE_TYPES.BOT,
text: 'История очищена. Чем могу помочь теперь?',
timestamp: new Date(),
agent: selectedAgent.id
}
]);
};
// Обработка нажатия Enter
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
// Форматирование времени
const formatTime = (date) => {
return format(date, 'HH:mm', { locale: ru });
};
return (
<div className="chat-app">
{/* Заголовок */}
<div className="chat-header">
<div className="chat-title">
<h1>💬 ИИ-Чат ассистент</h1>
<p>Общайтесь с искусственным интеллектом</p>
</div>
<button className="clear-btn" onClick={clearChat} title="Очистить историю">
<FiTrash2 /> Очистить
</button>
</div>
<div className="chat-container">
{/* Панель выбора агента */}
<div className="agent-selector">
<h3>🎭 Выберите ИИ-агента:</h3>
<div className="agent-options">
{AI_AGENTS.map(agent => (
<div
key={agent.id}
className={`agent-option ${selectedAgent.id === agent.id ? 'selected' : ''}`}
onClick={() => setSelectedAgent(agent)}
>
<div className="agent-icon">{agent.name.split(' ')[0]}</div>
<div className="agent-info">
<h4>{agent.name}</h4>
<p>{agent.description}</p>
</div>
</div>
))}
</div>
<div className="current-agent-info">
<h4>Текущий агент:</h4>
<div className="current-agent">
<span className="agent-badge">{selectedAgent.name.split(' ')[0]}</span>
<span>{selectedAgent.name}</span>
</div>
</div>
</div>
{/* Окно чата */}
<div className="chat-window">
<div className="messages-container">
{messages.map(message => (
<div
key={message.id}
className={`message ${message.type}`}
>
<div className="message-header">
<div className="message-sender">
{message.type === MESSAGE_TYPES.USER ? (
<>
<FiUser /> Вы
</>
) : (
<>
<FiBot /> {AI_AGENTS.find(a => a.id === message.agent)?.name.split(' ')[0]}
</>
)}
</div>
<div className="message-time">
{formatTime(message.timestamp)}
</div>
</div>
<div className="message-content">
{message.text}
</div>
{message.type === MESSAGE_TYPES.BOT && (
<div className="message-footer">
<span className="agent-tag">
{AI_AGENTS.find(a => a.id === message.agent)?.name}
</span>
</div>
)}
</div>
))}
{isTyping && (
<div className="message bot typing">
<div className="message-header">
<div className="message-sender">
<FiBot /> {selectedAgent.name.split(' ')[0]}
</div>
</div>
<div className="message-content typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
{/* Поле ввода */}
<div className="input-area">
<textarea
value={inputText}
onChange={(e) => setInputText(e.target.value)}
onKeyPress={handleKeyPress}
placeholder={`Введите сообщение для ${selectedAgent.name}...`}
rows="3"
className="message-input"
/>
<button
onClick={handleSendMessage}
disabled={!inputText.trim() || isTyping}
className="send-button"
>
<FiSend /> Отправить
</button>
</div>
</div>
</div>
{/* Статистика */}
<div className="chat-stats">
<div className="stat">
<span className="stat-label">Сообщений:</span>
<span className="stat-value">{messages.length}</span>
</div>
<div className="stat">
<span className="stat-label">Текущий агент:</span>
<span className="stat-value">{selectedAgent.name}</span>
</div>
<div className="stat">
<span className="stat-label">Статус:</span>
<span className="stat-value">
{isTyping ? 'ИИ печатает...' : 'Готов к общению'}
</span>
</div>
</div>
</div>
);
}
export default ChatApp;
Добавляем стили чата:
css
/* chat-app/src/App.css */
.chat-app {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
}
/* Заголовок */
.chat-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
background: white;
padding: 1.5rem;
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.chat-title h1 {
margin: 0;
color: #333;
font-size: 2rem;
}
.chat-title p {
margin: 0.5rem 0 0 0;
color: #666;
}
.clear-btn {
background: #ff4757;
color: white;
border: none;
padding: 0.8rem 1.5rem;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: bold;
transition: all 0.3s ease;
}
.clear-btn:hover {
background: #ff3742;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 71, 87, 0.3);
}
/* Контейнер чата */
.chat-container {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 2rem;
margin-bottom: 2rem;
}
/* Селектор агента */
.agent-selector {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.agent-selector h3 {
margin-top: 0;
margin-bottom: 1.5rem;
color: #333;
}
.agent-options {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 2rem;
}
.agent-option {
padding: 1rem;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid #e0e0e0;
display: flex;
align-items: center;
gap: 1rem;
}
.agent-option:hover {
border-color: #4a90e2;
background: #f0f7ff;
transform: translateX(5px);
}
.agent-option.selected {
border-color: #4a90e2;
background: #e3f2fd;
box-shadow: 0 2px 8px rgba(74, 144, 226, 0.2);
}
.agent-icon {
font-size: 1.5rem;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
color: white;
}
.agent-info h4 {
margin: 0;
color: #333;
}
.agent-info p {
margin: 0.2rem 0 0 0;
color: #666;
font-size: 0.9rem;
}
.current-agent-info {
border-top: 1px solid #eee;
padding-top: 1.5rem;
}
.current-agent-info h4 {
margin: 0 0 1rem 0;
color: #333;
}
.current-agent {
display: flex;
align-items: center;
gap: 1rem;
}
.agent-badge {
background: linear-gradient(135deg, #4CAF50, #8BC34A);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: bold;
}
/* Окно чата */
.chat-window {
background: white;
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
height: 600px;
}
.messages-container {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Сообщения */
.message {
max-width: 80%;
padding: 1rem;
border-radius: 15px;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message.user {
align-self: flex-end;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-bottom-right-radius: 5px;
}
.message.bot {
align-self: flex-start;
background: #f0f2f5;
color: #333;
border-bottom-left-radius: 5px;
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.message-sender {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: bold;
}
.message-time {
color: #999;
font-size: 0.8rem;
}
.message-content {
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
.message-footer {
margin-top: 0.5rem;
display: flex;
justify-content: flex-end;
}
.agent-tag {
background: rgba(74, 144, 226, 0.1);
color: #4a90e2;
padding: 0.2rem 0.8rem;
border-radius: 12px;
font-size: 0.8rem;
font-weight: bold;
}
/* Индикатор набора */
.typing-indicator {
display: flex;
gap: 0.3rem;
align-items: center;
}
.typing-indicator span {
width: 8px;
height: 8px;
background: #999;
border-radius: 50%;
animation: typing 1.4s infinite ease-in-out;
}
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
@keyframes typing {
0%, 80%, 100% { transform: scale(0.5); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
/* Поле ввода */
.input-area {
border-top: 1px solid #eee;
padding: 1.5rem;
display: flex;
gap: 1rem;
}
.message-input {
flex: 1;
padding: 1rem;
border: 2px solid #e0e0e0;
border-radius: 10px;
font-family: inherit;
font-size: 1rem;
resize: none;
transition: border-color 0.3s ease;
}
.message-input:focus {
outline: none;
border-color: #4a90e2;
}
.send-button {
background: linear-gradient(135deg, #4CAF50, #8BC34A);
color: white;
border: none;
padding: 0 2rem;
border-radius: 10px;
cursor: pointer;
font-weight: bold;
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
}
.send-button:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
}
.send-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Статистика */
.chat-stats {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
display: flex;
justify-content: space-around;
}
.stat {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.stat-label {
color: #666;
font-size: 0.9rem;
}
.stat-value {
font-size: 1.2rem;
font-weight: bold;
color: #333;
}
Запускаем чат отдельно:
bash
# В папке chat-app
npm run dev
Открываем в браузере: http://localhost:5175
Что должно получиться:
✅ Полноценный чат с выбором ИИ-агентов
✅ Анимации набора текста
✅ История сообщений с временными метками
✅ Возможность очистки чата
✅ Статистика в реальном времени
На что обратить внимание:
- Ответы ИИ имитируются
- В реальном проекте будет WebSocket соединение
- Сообщения сохраняются только в памяти (при перезагрузке пропадут)
- Можно выбрать разных ИИ-агентов
🧩 Шаг 5: Объединяем микрофронтенды в контейнер
Цель шага: Собрать все независимые приложения в одно целое.
Что такое "контейнерное приложение":
Это главное приложение, которое загружает и отображает микрофронтенды. Как рама для картин — можно менять картины, не меняя раму.
Создаём контейнерное приложение:
bash
# Возвращаемся в папку проекта
cd ..
npm create vite@latest container -- --template react
cd container
npm install
# Устанавливаем модуль-федерацию для Webpack
npm install @module-federation/enhanced
# Или используем готовый шаблон с микрофронтендами:
# npx @module-federation/create-mf-app
Настраиваем Module Federation (объединение модулей):
Объяснение термина: Module Federation — технология, позволяющая загружать код из других приложений во время выполнения. Как если бы разные программы могли делиться своими частями.
javascript
// container/vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@module-federation/enhanced/vite'
export default defineConfig({
plugins: [
react(),
federation({
name: 'container',
remotes: {
header: 'http://localhost:5173/assets/remoteEntry.js',
dashboard: 'http://localhost:5174/assets/remoteEntry.js',
chat: 'http://localhost:5175/assets/remoteEntry.js',
},
shared: ['react', 'react-dom']
})
],
build: {
target: 'esnext'
}
})
Настраиваем микрофронтенды для экспорта:
javascript
// header-app/vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@module-federation/enhanced/vite'
export default defineConfig({
plugins: [
react(),
federation({
name: 'header',
filename: 'remoteEntry.js',
exposes: {
'./Header': './src/App'
},
shared: ['react', 'react-dom']
})
],
build: {
target: 'esnext'
}
})
javascript
// dashboard-app/vite.config.js и chat-app/vite.config.js - аналогично
Создаём главный контейнер:
jsx
// container/src/App.jsx
import { Suspense, lazy } from 'react';
import './App.css';
// Динамически загружаем микрофронтенды
const Header = lazy(() => import('header/Header'));
const Dashboard = lazy(() => import('dashboard/Dashboard'));
const Chat = lazy(() => import('chat/Chat'));
function App() {
return (
<div className="container-app">
{/* Загрузчик для микрофронтендов */}
<Suspense fallback={<div className="loading">🌀 Загрузка приложения...</div>}>
<Header />
</Suspense>
<main className="main-content">
<div className="left-panel">
<Suspense fallback={<div className="loading">📊 Загрузка дашборда...</div>}>
<Dashboard />
</Suspense>
</div>
<div className="right-panel">
<Suspense fallback={<div className="loading">💬 Загрузка чата...</div>}>
<Chat />
</Suspense>
</div>
</main>
<footer className="app-footer">
<p>ИИ-Платформа 2024 | Все микрофронтенды работают независимо</p>
<div className="footer-info">
<span>🚀 Загружено: 3 микрофронтенда</span>
<span>⚡ Версия контейнера: 1.0.0</span>
</div>
</footer>
</div>
);
}
export default App;
Стили контейнера:
css
/* container/src/App.css */
.container-app {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Загрузчик */
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
font-size: 1.2rem;
color: #666;
background: #f5f5f5;
border-radius: 10px;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Основной контент */
.main-content {
display: flex;
flex: 1;
padding: 2rem;
gap: 2rem;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.left-panel {
flex: 2;
min-width: 0; /* Для правильного скролла */
}
.right-panel {
flex: 1;
min-width: 0;
min-height: 600px;
}
/* Футер */
.app-footer {
background: #333;
color: white;
padding: 1.5rem 2rem;
text-align: center;
}
.app-footer p {
margin: 0 0 1rem 0;
font-size: 1.1rem;
}
.footer-info {
display: flex;
justify-content: center;
gap: 3rem;
font-size: 0.9rem;
color: #ccc;
}
Настраиваем package.json для CORS (разрешаем доступ):
json
{
"scripts": {
"dev": "vite --host --port 3000",
"build": "vite build",
"preview": "vite preview"
}
}
Запускаем все приложения:
- В терминале 1: cd header-app && npm run dev -- --port 5173 --host
- В терминале 2: cd dashboard-app && npm run dev -- --port 5174 --host
- В терминале 3: cd chat-app && npm run dev -- --port 5175 --host
- В терминале 4: cd container && npm run dev -- --port 3000 --host
Что должно получиться:
✅ Все 4 приложения запущены
✅ Контейнер на localhost:3000 загружает 3 микрофронтенда
✅ Можно обновлять каждый микрофронтенд отдельно
✅ Стили не конфликтуют
✅ Ленивая загрузка (подгружаются по мере необходимости)
На что обратить внимание:
- Module Federation требует настройки CORS
- Все приложения должны быть запущены
- В production нужно собирать статичные файлы
- Можно использовать разные версии React в микрофронтендах
🔄 Шаг 6: Управление версиями микрофронтендов
Цель шага: Научиться обновлять части приложения независимо друг от друга.
Что такое "семантическое версионирование":
Формат версий: MAJOR.MINOR.PATCH (например: 2.1.3)
- MAJOR — ломающие изменения
- MINOR — новые функции, обратная совместимость
- PATCH — исправления багов
Создаём систему управления версиями:
json
// В каждом микрофронтенде создаём version.json
// header-app/public/version.json
{
"name": "header-app",
"version": "1.2.0",
"buildDate": "2024-01-15T12:00:00Z",
"changelog": [
"Добавлена анимация логотипа",
"Исправлена адаптивность на мобильных",
"Добавлен tooltip к кнопкам"
],
"compatibility": {
"container": ">=1.0.0",
"react": "^18.0.0"
}
}
Добавляем компонент для отображения версий:
jsx
// container/src/components/VersionManager.jsx
import { useState, useEffect } from 'react';
import './VersionManager.css';
function VersionManager() {
const [versions, setVersions] = useState({});
const [updateAvailable, setUpdateAvailable] = useState(false);
// Загружаем версии микрофронтендов
useEffect(() => {
const loadVersions = async () => {
try {
const mfUrls = {
header: 'http://localhost:5173/version.json',
dashboard: 'http://localhost:5174/version.json',
chat: 'http://localhost:5175/version.json'
};
const results = {};
for (const [name, url] of Object.entries(mfUrls)) {
try {
const response = await fetch(url);
const data = await response.json();
results[name] = data;
} catch (error) {
results[name] = {
name,
version: 'Недоступно',
error: true
};
}
}
setVersions(results);
// Проверяем обновления
checkForUpdates(results);
} catch (error) {
console.error('Ошибка загрузки версий:', error);
}
};
loadVersions();
// Проверяем каждые 5 минут
const interval = setInterval(loadVersions, 5 * 60 * 1000);
return () => clearInterval(interval);
}, []);
const checkForUpdates = (currentVersions) => {
// Здесь могла бы быть логика сравнения с последними версиями
// Например, запрос к API с доступными обновлениями
const hasUpdate = Object.values(currentVersions).some(
v => v.latestVersion && v.latestVersion !== v.version
);
setUpdateAvailable(hasUpdate);
};
const handleUpdate = (appName) => {
// В реальном проекте здесь была бы логика обновления
alert(`Обновление ${appName}... В реальном проекте здесь был бы механизм обновления`);
// Имитация обновления
window.location.reload();
};
return (
<div className="version-manager">
<div className="version-header">
<h3>📦 Управление версиями</h3>
{updateAvailable && (
<span className="update-badge">Доступны обновления!</span>
)}
</div>
<div className="version-list">
{Object.entries(versions).map(([name, info]) => (
<div key={name} className="version-item">
<div className="version-info">
<span className="app-name">{info.name || name}</span>
<span className={`app-version ${info.error ? 'error' : ''}`}>
{info.version} {info.error && '❌'}
</span>
{info.buildDate && (
<span className="build-date">
{new Date(info.buildDate).toLocaleDateString()}
</span>
)}
</div>
<div className="version-actions">
{info.error ? (
<button className="btn-error">Ошибка загрузки</button>
) : (
<>
<button
className="btn-details"
onClick={() => alert(JSON.stringify(info, null, 2))}
>
Детали
</button>
<button
className="btn-update"
onClick={() => handleUpdate(name)}
>
Обновить
</button>
</>
)}
</div>
</div>
))}
</div>
<div className="version-notes">
<h4>🎯 Преимущества версионности:</h4>
<ul>
<li>Каждый микрофронтенд обновляется независимо</li>
<li>Можно откатить одну часть без влияния на другие</li>
<li>A/B тестирование разных версий</li>
<li>Постепенное развертывание новых функций</li>
</ul>
</div>
</div>
);
}
export default VersionManager;
Стили менеджера версий:
css
/* VersionManager.css */
.version-manager {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
margin: 2rem;
}
.version-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
border-bottom: 2px solid #f0f0f0;
padding-bottom: 1rem;
}
.version-header h3 {
margin: 0;
color: #333;
}
.update-badge {
background: linear-gradient(135deg, #4CAF50, #8BC34A);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.9rem;
font-weight: bold;
animation: pulse 2s infinite;
}
.version-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.version-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: #f8f9fa;
border-radius: 10px;
border-left: 4px solid #4a90e2;
transition: all 0.3s ease;
}
.version-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.version-info {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.app-name {
font-weight: bold;
color: #333;
font-size: 1.1rem;
}
.app-version {
color: #666;
font-family: monospace;
}
.app-version.error {
color: #f44336;
}
.build-date {
font-size: 0.9rem;
color: #999;
}
.version-actions {
display: flex;
gap: 1rem;
}
.btn-details, .btn-update, .btn-error {
padding: 0.5rem 1rem;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
}
.btn-details {
background: #e3f2fd;
color: #1976d2;
}
.btn-details:hover {
background: #bbdefb;
}
.btn-update {
background: linear-gradient(135deg, #4CAF50, #8BC34A);
color: white;
}
.btn-update:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
}
.btn-error {
background: #ffebee;
color: #c62828;
cursor: not-allowed;
}
.version-notes {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 2px solid #f0f0f0;
}
.version-notes h4 {
margin: 0 0 1rem 0;
color: #333;
}
.version-notes ul {
margin: 0;
padding-left: 1.5rem;
color: #666;
line-height: 1.6;
}
.version-notes li {
margin-bottom: 0.5rem;
}
Что должно получиться:
✅ Панель управления версиями всех микрофронтендов
✅ Видно, какая версия каждого приложения загружена
✅ Можно проверять обновления
✅ Отдельные кнопки обновления для каждого приложения
На что обратить внимание:
- В production версии хранятся в CDN
- Нужен механизм безопасного обновления (feature flags)
- Можно делать канареечные развертывания
- Важно тестировать совместимость версий
🏗️ Шаг 7: Архитектура и deployment (развертывание)
Цель шага: Понять, как развернуть микрофронтенды в production.
Структура проекта для production:
text
production/
├── nginx/ # Веб-сервер для проксирования
│ └── nginx.conf
├── docker-compose.yml # Оркестрация контейнеров
├── container/ # Собранный контейнер
├── header-app/ # Собранный header
├── dashboard-app/ # Собранный dashboard
└── chat-app/ # Собранный chat
Конфигурация Nginx:
nginx
# nginx/nginx.conf
events {
worker_connections 1024;
}
http {
# Контейнерное приложение
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://container:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
# Микрофронтенды (каждый на своём поддомене)
server {
listen 80;
server_name header.example.com;
location / {
proxy_pass http://header-app:5173;
# ... те же настройки proxy
}
}
server {
listen 80;
server_name dashboard.example.com;
location / {
proxy_pass http://dashboard-app:5174;
}
}
server {
listen 80;
server_name chat.example.com;
location / {
proxy_pass http://chat-app:5175;
}
}
}
Dockerfile для микрофронтенда:
dockerfile
# header-app/Dockerfile
# Шаг 1: Сборка
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Шаг 2: Продакшен
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Docker Compose для запуска всего:
yaml
# docker-compose.yml
version: '3.8'
services:
# Контейнерное приложение
container:
build: ./container
ports:
- "3000:80"
environment:
- NODE_ENV=production
depends_on:
- header-app
- dashboard-app
- chat-app
# Микрофронтенды
header-app:
build: ./header-app
ports:
- "5173:80"
environment:
- NODE_ENV=production
dashboard-app:
build: ./dashboard-app
ports:
- "5174:80"
environment:
- NODE_ENV=production
chat-app:
build: ./chat-app
ports:
- "5175:80"
environment:
- NODE_ENV=production
# Nginx для проксирования
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- container
- header-app
- dashboard-app
- chat-app
Скрипты для сборки и деплоя:
json
// package.json в корне проекта
{
"scripts": {
"build:all": "npm run build --workspaces",
"dev:all": "concurrently \"npm run dev --workspace=header-app\" \"npm run dev --workspace=dashboard-app\" \"npm run dev --workspace=chat-app\" \"npm run dev --workspace=container\"",
"deploy:staging": "docker-compose -f docker-compose.staging.yml up -d",
"deploy:production": "docker-compose -f docker-compose.prod.yml up -d",
"update:header": "cd header-app && npm run build && docker build -t header-app:latest .",
"rollback:header": "docker service update --image header-app:previous myapp_header-app"
},
"workspaces": [
"header-app",
"dashboard-app",
"chat-app",
"container"
]
}
Что должно получиться:
✅ Все приложения упакованы в Docker контейнеры
✅ Nginx проксирует запросы
✅ Можно деплоить каждый микрофронтенд отдельно
✅ Автоматическое масштабирование возможно
✅ Отказоустойчивость (если один упадёт, остальные работают)
На что обратить внимание:
- В production нужен SSL/TLS
- Важно настроить кэширование
- Мониторинг каждого микрофронтенда отдельно
- CI/CD пайплайн для каждого приложения
🎓 Итог: Что вы научились делать
✅ Вы освоили:
- Создавать микрофронтенды на React с помощью Vite
- Изолировать стили и логику каждого приложения
- Объединять через Module Federation
- Управлять версиями независимо
- Деплоить в production с Docker
🚀 Где хранить микрофронтенды:
- Git репозитории — отдельный репозиторий для каждого
- Docker Registry — образы контейнеров
- CDN — статические файлы для быстрой загрузки
- NPM Registry — если публикуете как библиотеки
🔧 Как управлять версионностью:
- Semantic Versioning — major.minor.patch
- Feature Flags — постепенное включение функций
- A/B тестирование — разные версии для разных пользователей
- Канареечные развертывания — сначала 1% трафика, потом больше
💻 На каком языке писать:
🎯 Для ИИ-агентов рекомендую:
- React — для сложных дашбордов и визуализаций
- Vue.js — для быстрого прототипирования интерфейсов
- Svelte — для высокопроизводительных компонентов
📚 Технологии, которые позволяют создавать микрофронтенды:
1. Module Federation (Webpack 5)
javascript
// Самый популярный подход
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'container',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js'
}
})
]
};
2. Single-spa
javascript
// Framework-agnostic роутер
import { registerApplication, start } from 'single-spa';
registerApplication({
name: 'app1',
app: () => System.import('app1'),
activeWhen: '/app1'
});
start();
3. Qiankun (от Alibaba)
javascript
// Основан на single-spa, но проще
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react-app',
entry: '//localhost:7100',
container: '#container',
activeRule: '/react'
}
]);
start();
4. iframe (самый простой)
html
<!-- Просто, но ограниченно -->
<iframe src="http://localhost:3001" width="100%" height="500"></iframe>
5. Web Components
javascript
// Нативные веб-компоненты
class MyElement extends HTMLElement {
connectedCallback() {
this.innerHTML = `<h1>Микрофронтенд</h1>`;
}
}
customElements.define('my-element', MyElement);
🎯 Следующие шаги для развития:
- Изучите State Management — как делиться состоянием между микрофронтендами
- Добавьте TypeScript — для типизации и лучшего DX
- Настройте мониторинг — Sentry, LogRocket для каждого приложения
- Оптимизируйте загрузку — lazy loading, code splitting
- Создайте дизайн-систему — общие компоненты для всех приложений
💡 Практические советы:
- Начинайте с малого — один микрофронтенд, потом добавляйте
- Тестируйте интеграцию — между микрофронтендами
- Стандартизируйте — код-стайл, линтеры, тесты
- Документируйте — API, версии, зависимости
⚠️ Чего избегать:
- ❌ Глобальных стилей без изоляции
- ❌ Прямых зависимостей между микрофронтендами
- ❌ Разных версий одних и тех же библиотек
- ❌ Сложной синхронизации состояния
Удачи в создании ваших ИИ-агентов с микрофронтендами! 🚀
❓ Часто задаваемые вопросы:
Q: Как общаться между микрофронтендами?
A: Через Custom Events, Window свойства или выделенную библиотеку (Redux, Zustand).
Q: Что делать с авторизацией?
A: Единый сервис аутентификации, токены в куках или localStorage.
Q: Как тестировать микрофронтенды?
A: Каждый отдельно + интеграционные тесты контейнера.
Q: SEO для микрофронтендов?
A: SSR для каждого приложения или pre-rendering.
Q: Как дебажить?
A: Source maps + отдельные dev серверы для каждого приложения.