Micro Frontends Module Federation Với Vite

Chia nhỏ ứng dụng khổng lồ thành các micro frontend độc lập với tốc độ build chớp nhoáng của Vite.

14/05/2026
12 phút đọc

Khi dự án của bạn lớn dần lên, một nhóm frontend duy nhất không thể ôm trọn mọi thứ. Codebase trở thành một "nồi lẩu thập cẩm", thời gian build kéo dài tính bằng chục phút, và mỗi lần Deploy là một nỗi ám ảnh vì xung đột mã nguồn.

Đó là lý do kiến trúc Micro Frontends ra đời. Nó cho phép chia nhỏ một ứng dụng khổng lồ thành nhiều ứng dụng con độc lập, được phát triển và Deploy bởi các nhóm khác nhau. Hôm nay, chúng ta sẽ kết hợp khái niệm Module Federation đình đám với "chiếc xe đua F1" của làng Web Tooling là Vite.

1

Lời Nguyền Của Monolith Frontend

Phần lớn các ứng dụng React/Vue/Angular truyền thống được xây dựng theo kiểu Monolithic (nguyên khối). Tức là tất cả các module: Xác thực (Auth), Giỏ hàng (Cart), Thanh toán (Checkout), Sản phẩm (Product)... đều nằm chung trong 1 kho chứa (Repository) và chung 1 quá trình Build.

  • Scale kém: 50 lập trình viên cùng làm việc trên 1 repo sẽ liên tục gặp Merge Conflict.
  • Build chậm: Dự án lớn dùng Webpack có thể mất tới 10-20 phút chỉ để ra file bundle.
  • Rủi ro Deploy: Team Checkout sửa 1 dòng code, nhưng phải test lại toàn bộ Team Auth, Team Cart rồi mới dám deploy toàn hệ thống.

Giải pháp: Micro Frontends. Tách UI thành các Repo riêng, deploy riêng, và "ghép" chúng lại trên trình duyệt của người dùng.

2

Module Federation Là Gì?

Ra mắt từ Webpack 5, Module Federation (MF) là kỹ thuật cho phép một ứng dụng JavaScript "tải và chạy" mã nguồn của một ứng dụng JavaScript khác ngay lúc Runtime (chạy trên trình duyệt), thay vì phải cài đặt qua NPM lúc Build-time.

Trong kiến trúc này, chúng ta có 2 khái niệm:

  • Host (Shell): Ứng dụng vỏ bọc bên ngoài. Nó chứa thanh điều hướng chung và load các ứng dụng con vào.
  • Remote (Micro-app): Ứng dụng con chứa một chức năng cụ thể (ví dụ: Trang Thanh toán). Nó phơi bày (expose) các component của nó ra ngoài.

Tuyệt vời hơn, Vite (thông qua Rollup) hoàn toàn có thể hỗ trợ chuẩn Module Federation này bằng các plugin bên thứ ba, cho phép ta vừa có kiến trúc xịn, vừa giữ được tốc độ HMR chớp nhoáng.

3

Cài Đặt Vite Plugin

Để mang tính năng này vào Vite, chúng ta sẽ sử dụng plugin rất nổi tiếng: @originjs/vite-plugin-federation.

Bắt đầu bằng việc khởi tạo 2 dự án Vite React độc lập:

# Khởi tạo Host App
npm create vite@latest host-app -- --template react-ts
# Khởi tạo Remote App
npm create vite@latest remote-app -- --template react-ts

# Cài đặt plugin cho cả hai
npm i -D @originjs/vite-plugin-federation
4

Cấu Hình Remote App

Mở file vite.config.ts của dự án remote-app. Nhiệm vụ của chúng ta là phải "xuất" (expose) một component (ví dụ: Button) ra cho thế giới bên ngoài dùng.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'remote_app',
      filename: 'remoteEntry.js', // File entry point mà Host sẽ gọi
      exposes: {
        './Button': './src/components/Button.tsx', // Xuất component Button
        './CartApp': './src/App.tsx'               // Có thể xuất cả 1 trang App
      },
      shared: ['react', 'react-dom']
    })
  ],
  build: {
    target: 'esnext' // Yêu cầu bắt buộc để chạy MF trên Vite
  }
});

Khởi động remote app ở một cổng cố định, ví dụ 5001.

5

Cấu Hình Host App

Bây giờ, sang vite.config.ts của host-app. Chúng ta sẽ "nhập" (remotes) cái remoteEntry.js từ server 5001 vừa rồi.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'host_app',
      remotes: {
        // Tên tham chiếu: URL tới file entry của remote
        remoteApp: 'http://localhost:5001/assets/remoteEntry.js'
      },
      shared: ['react', 'react-dom']
    })
  ],
  build: {
    target: 'esnext'
  }
});

Cách sử dụng trong React (Host):

import React, { lazy, Suspense } from 'react';

// Import component từ Remote (Tên_tham_chiếu/Tên_Export)
// Phải dùng lazy load vì mã này lấy từ mạng (Network) về
const RemoteButton = lazy(() => import('remoteApp/Button'));

function App() {
  return (
    <div>
      <h1>Đây là Host App (Vỏ ngoài)</h1>
      
      <Suspense fallback={<div>Đang tải Button từ Remote...</div>}>
        <RemoteButton onClick={() => alert('Alo!')} />
      </Suspense>
    </div>
  );
}
export default App;

Khi chạy Host App, trình duyệt sẽ tự động bắn 1 request tải file remoteEntry.js từ port 5001, parse và nạp Component Button vào DOM một cách thần kỳ!

6

Chia Sẻ Dependencies (Tối Ưu Hóa)

Bạn có để ý mảng shared: ['react', 'react-dom'] ở cả Host và Remote không? Đó là một tính năng cực kỳ thông minh của Module Federation.

Khi Host App đã tải thư viện React từ mạng về, và sau đó nó tiếp tục gọi Remote App (vốn cũng được viết bằng React), thì trình duyệt sẽ KHÔNG tải lại React lần thứ 2!

Module Federation sẽ kiểm tra phiên bản (version) của các thư viện dùng chung. Nếu chúng tương thích, Remote App sẽ tái sử dụng luôn instance React của Host App. Điều này giúp giảm hàng trăm KB dung lượng tải xuống và giữ nguyên trạng thái React Context an toàn.

7

Lưu Ý Về CSS Isolation (Cách ly CSS)

Micro Frontends mang lại kiến trúc tuyệt vời, nhưng nhược điểm lớn nhất của nó là CSS Leak (Rò rỉ CSS).

Vì cả Host và Remote cuối cùng đều render lên cùng 1 cây DOM của Trình duyệt. Nếu Remote App có một đoạn CSS h1 { color: red; }, thì tất cả các thẻ H1 của Host App cũng sẽ bị biến thành màu đỏ!

Giải pháp:
  • Sử dụng CSS Modules: Đảm bảo mọi tên class đều được băm (hash) duy nhất .Button_xyz123.
  • Tailwind Prefix: Nếu dùng Tailwind, hãy đặt prefix: 'remote-' trong tailwind.config.js của ứng dụng con để tránh đụng độ class với Host.
  • Web Components (Shadow DOM): Đây là mức độ cách ly cao nhất nhưng sẽ khó kết nối với React Context.

Micro Frontends là một kiến trúc rất phức tạp. Hãy chỉ sử dụng nó khi ứng dụng của bạn đủ lớn (hàng chục dev) và có nghiệp vụ cần chia cắt rõ ràng. Đừng lạm dụng nó cho các dự án nhỏ vì nó sẽ biến hệ thống CI/CD của bạn thành ác mộng. Chúc bạn thành công với kiến trúc này trên Vite!

Bài viết liên quan

Bẻ Khóa TypeScript: Mapped Types & Conditional Types

Làm chủ các khái niệm nâng cao trong TypeScript để xây dựng các thư viện an toàn kiểu dữ liệu (type-safe) 100%.

14/05/2026
14 phút đọc

Giao Diện Sống Động Với Framer Motion

Hướng dẫn từng bước thêm các tương tác vi mô (microinteractions) dạng lò xo vào component để ứng dụng của bạn cho cảm giác cao cấp và phản hồi tốt hơn.

14/05/2026
15 phút đọc