Mục lục

Performance & Resilience

Tối ưu hóa: Prefetching, Error Boundaries và Performance Monitoring.

Làm thế nào để hệ thống MFE của bạn vẫn nhanh như chớp và không bao giờ "chết" hoàn toàn khi một phần server bị sập?

1. Phân tích thiết kế: Khả năng tự phục hồi (Resilience)

Cách làm truyền thống: Single Point of Failure

Trong Monolith, nếu một đoạn code JS gặp lỗi runtime nghiêm trọng, toàn bộ ứng dụng sẽ bị crash trắng trang.

Nhược điểm:

  • Lỗi dây chuyền: Một lỗi nhỏ ở phần "Gợi ý sản phẩm" có thể khiến người dùng không thể nhấn nút "Thanh toán".
  • Trải nghiệm người dùng kém: Người dùng không có cách nào tương tác tiếp với các phần còn lại của trang web.

Cách làm mới: Error Boundaries & Fallbacks

Mỗi MFE được bao bọc trong một "lồng bảo vệ" (Error Boundary). Nếu một Remote bị lỗi, Shell App sẽ tự động hiển thị một giao diện thay thế nhẹ nhàng.

Ưu điểm & Vấn đề giải quyết:

  • Graceful Degradation: Trang web vẫn chạy được các phần quan trọng (Checkout, Auth) ngay cả khi phần phụ (Live Stream, Ads) bị sập.
  • Isolate Failure: Lỗi được cô lập chặt chẽ trong phạm vi của Remote đó.

2. Prefetching Remotes

Để giải quyết độ trễ mạng khi tải các file remoteEntry.js, chúng ta sử dụng chiến lược Predictive Loading.

Kỹ thuật:

  • Tải MFE ngay khi người dùng hover vào menu.
  • Hoặc dùng Service Workers để cache trước các Remote quan trọng trong thời gian máy tính đang rảnh (Idle time).

3. Đánh đổi (Trade-offs)

  • Dư thừa tài nguyên: Việc tải trước (Prefetch) có thể làm tốn băng thông của người dùng nếu họ không thực sự vào trang đó.
  • Phức tạp trong Monitoring: Cần hệ thống tracking cực kỳ chi tiết để biết chính xác MFE nào đang gây chậm (TTI) cho người dùng.

4. Code minh họa: Error Boundary & Fallback

tsx:
// Shell App
import { ErrorBoundary } from 'react-error-boundary';

const RemoteCatalog = React.lazy(() => import('catalog/App'));

const CatalogSection = () => (
  <ErrorBoundary
    fallback={<div className="error-card">Tính năng Catalog tạm thời bảo trì</div>}
    onError={(error) => console.error("MFE Catalog Error:", error)}
  >
    <Suspense fallback={<Skeleton />}>
      <RemoteCatalog />
    </Suspense>
  </ErrorBoundary>
);

Bài tập: Simulate Down

Thực hiện các bước sau để test tính ổn định:

  1. Khởi chạy App Shell và Remote Catalog.
  2. Tắt server của Remote Catalog (giết process).
  3. Yêu cầu: App Shell vẫn phải hiển thị Header/Footer và hiển thị một thông báo "Section Catalog đang bảo trì" (Fallback UI) thay vì crash cả trang web.
  4. Nâng cao: Thêm cơ chế Retry tự động (Vd: cứ 5 giây thử tải lại Remote Catalog một lần).

Câu hỏi phỏng vấn

Q: Làm sao để tối ưu hóa chỉ số LCP (Largest Contentful Paint) trong Microfrontends?

  • Trả lời: LCP thường bị ảnh hưởng bởi việc phải tải nhiều file JS trước khi render.
    1. SSR: Thực hiện render HTML tại server (Server-side Federation).
    2. Prefetch: Tải trước file remoteEntry.js của các Remote quan trọng ngay sau khi Main app khởi chạy.
    3. Critical Assets: Đảm bảo các ảnh LCP (Vd: banner trang chủ) không nằm trong một Remote được tải quá muộn.
    4. Deduplication: Dùng Module Federation để đảm bảo không tải lại các thư viện lớn như React/Lodash nhiều lần.

Q: Điều gì xảy ra nếu một Remote MFE mất 10 giây mới phản hồi?

  • Trả lời: Đây là kịch bản tồi tệ. Chúng ta phải áp dụng TimoutsFallbacks.
    1. Sử dụng Promise.race để giới hạn thời gian tải Remote (Vd: sau 2s không có code thì báo lỗi).
    2. Sử dụng ErrorBoundary để hiển thị một giao diện thay thế (Offline mode).
    3. Tránh việc các Remote "chặn" (blocking) việc hiển thị của App Shell.

Q: Bạn đo lường hiệu năng của từng Microfrontend như thế nào?

  • Trả lời: Chúng ta không thể chỉ nhìn vào Google Lighthouse của trang tổng. Chúng ta cần đo:
    • Asset waterfall: Thời gian tải của từng remoteEntry.js.
    • Hydration time: Thời gian React tiêu tốn để khởi tạo từng component từ CDN.
    • Custom Metrics: Dùng Performance.mark()Performance.measure() gắn vào từng MFE để gửi dữ liệu về hệ thống giám sát (Vd: Datadog, Grafana). flagship.
Quảng cáo
mdhorizontal