Настройка Module Federation (Vite) для микрофронтенда

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка Module Federation (Vite) для микрофронтенда
Сложная
~3-5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Настройка Module Federation (Vite) для микрофронтенда

Vite нативно не поддерживает Module Federation — в отличие от Webpack 5. Решение — плагин @originjs/vite-plugin-federation (или vite-plugin-federation), который реализует тот же протокол, что и Webpack MF, с поддержкой обоих бандлеров на разных концах.

Есть ограничения: Vite в dev-режиме не поддерживает живую загрузку federated модулей так же гладко, как Webpack — remote нужно билдить перед использованием в host. Для production всё работает идентично.

Что входит в работу

Настройка vite-plugin-federation в host и remote приложениях, shared-зависимости, TypeScript, dev-workflow с pre-build remote, CI/CD независимый деплой, обработка ошибок, коммуникация между microfrontends.

Установка

npm install -D @originjs/vite-plugin-federation

vite.config.ts — Remote (catalog)

// apps/catalog/vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@originjs/vite-plugin-federation'

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'catalog',
      filename: 'remoteEntry.js',
      exposes: {
        './ProductList': './src/components/ProductList',
        './ProductDetail': './src/components/ProductDetail',
        './ProductCard': './src/components/ProductCard',
        './useProducts': './src/hooks/useProducts',
      },
      shared: ['react', 'react-dom', 'react-router-dom'],
    }),
  ],
  build: {
    target: 'esnext',
    minify: false,        // важно для federation
    cssCodeSplit: false,  // предотвращает проблемы с CSS в federated модулях
  },
  preview: {
    port: 3001,
    host: true,
  },
})

vite.config.ts — Host (shell)

// apps/shell/vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import federation from '@originjs/vite-plugin-federation'

const REMOTES = {
  development: {
    catalog: 'http://localhost:3001/assets/remoteEntry.js',
    checkout: 'http://localhost:3002/assets/remoteEntry.js',
    auth: 'http://localhost:3003/assets/remoteEntry.js',
  },
  production: {
    catalog: 'https://catalog.example.com/assets/remoteEntry.js',
    checkout: 'https://checkout.example.com/assets/remoteEntry.js',
    auth: 'https://auth.example.com/assets/remoteEntry.js',
  },
}

export default defineConfig(({ mode }) => ({
  plugins: [
    react(),
    federation({
      name: 'shell',
      remotes: REMOTES[mode as keyof typeof REMOTES] ?? REMOTES.development,
      shared: ['react', 'react-dom', 'react-router-dom'],
    }),
  ],
  build: {
    target: 'esnext',
    minify: false,
  },
  server: {
    port: 3000,
  },
}))

TypeScript-декларации для remote

Плагин не генерирует типы автоматически. Нужно либо публиковать типы через npm-пакет, либо описывать вручную:

// apps/shell/src/types/remotes.d.ts
declare module 'catalog/ProductList' {
  import type { FC } from 'react'
  interface Props {
    categoryId: string
    onProductClick?: (id: string) => void
  }
  const ProductList: FC<Props>
  export default ProductList
}

declare module 'catalog/ProductDetail' {
  import type { FC } from 'react'
  interface Props {
    productId: string
  }
  const ProductDetail: FC<Props>
  export default ProductDetail
}

declare module 'checkout/CheckoutFlow' {
  import type { FC } from 'react'
  const CheckoutFlow: FC
  export default CheckoutFlow
}

Более поддерживаемый подход — экспортировать типы из remote в виде npm-пакета:

packages/
  catalog-types/
    index.d.ts
    package.json
// packages/catalog-types/index.d.ts
declare module 'catalog/ProductList' {
  export { default } from '@catalog/types/components/ProductList'
}

Bootstrap pattern

// src/index.ts
import('./bootstrap')

// src/bootstrap.tsx
import React from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'

createRoot(document.getElementById('root')!).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
)

Использование remote в компоненте

// apps/shell/src/App.tsx
import React, { Suspense, lazy } from 'react'
import { Routes, Route } from 'react-router-dom'
import { RemoteErrorBoundary } from './components/RemoteErrorBoundary'

const ProductList = lazy(() => import('catalog/ProductList'))
const ProductDetail = lazy(() => import('catalog/ProductDetail'))
const CheckoutFlow = lazy(() => import('checkout/CheckoutFlow'))

const RemoteFallback = ({ name }: { name: string }) => (
  <div>Приложение "{name}" временно недоступно</div>
)

function App() {
  return (
    <Routes>
      <Route
        path="/products"
        element={
          <RemoteErrorBoundary fallback={<RemoteFallback name="Каталог" />}>
            <Suspense fallback={<PageSkeleton />}>
              <ProductList categoryId="all" />
            </Suspense>
          </RemoteErrorBoundary>
        }
      />
      <Route
        path="/products/:id"
        element={
          <RemoteErrorBoundary fallback={<RemoteFallback name="Каталог" />}>
            <Suspense fallback={<PageSkeleton />}>
              <ProductDetailWrapper />
            </Suspense>
          </RemoteErrorBoundary>
        }
      />
    </Routes>
  )
}

Dev-workflow

Проблема Vite: remote нужно собрать перед запуском host в dev-режиме. Организуем через package.json в корне монорепо:

// package.json (monorepo root)
{
  "scripts": {
    "dev": "concurrently -n CATALOG,CHECKOUT,SHELL \"npm:dev:*\"",
    "dev:catalog": "cd apps/catalog && vite build --watch",
    "dev:checkout": "cd apps/checkout && vite build --watch",
    "dev:shell": "cd apps/shell && vite",
    "preview:catalog": "cd apps/catalog && vite build && vite preview",
    "preview:checkout": "cd apps/checkout && vite build && vite preview"
  }
}
npm install -D concurrently

Remote собираются в --watch режиме и сервируются через vite preview. Shell в обычном vite dev-режиме с HMR.

Динамические remotes

Когда URL remote нужно получить из конфигурации, а не захардкодить в vite.config:

// apps/shell/src/lib/dynamicImport.ts
export async function loadRemote<T>(
  remoteName: string,
  moduleName: string
): Promise<T> {
  const config = await fetch('/api/mf-config').then((r) => r.json())
  const remoteUrl = config[remoteName]

  if (!remoteUrl) throw new Error(`Remote ${remoteName} not configured`)

  // динамически загружаем remoteEntry
  await new Promise<void>((resolve, reject) => {
    const script = document.createElement('script')
    script.src = remoteUrl
    script.onload = () => resolve()
    script.onerror = reject
    document.head.appendChild(script)
  })

  // @ts-ignore — глобал, созданный remoteEntry
  const container = window[remoteName]
  await container.init(__webpack_share_scopes__.default)
  const factory = await container.get(moduleName)
  return factory()
}

Shared state

// packages/shared-store/index.ts
import { create } from 'zustand'

interface SharedState {
  cart: CartItem[]
  addToCart: (item: CartItem) => void
  user: User | null
}

export const useSharedStore = create<SharedState>((set) => ({
  cart: [],
  addToCart: (item) => set((s) => ({ cart: [...s.cart, item] })),
  user: null,
}))
// vite.config.ts (и host, и remotes)
shared: {
  react: { requiredVersion: '^18.0.0' },
  'react-dom': { requiredVersion: '^18.0.0' },
  '@company/shared-store': { singleton: true, requiredVersion: '*' },
}

Что делаем

Настраиваем vite-plugin-federation в каждом приложении, организуем dev-workflow с --watch сборкой remotes, описываем TypeScript-декларации, настраиваем shared-зависимости без конфликтов версий, выстраиваем pipeline независимого деплоя через GitHub Actions + CDN.

Срок: 5–8 дней, включая настройку dev-окружения и CI/CD.