Webpack · Part 11 — Module Federation
Share code between independently deployed apps at runtime: hosts, remotes, exposes, remotes, and the shared singleton trick that stops React being downloaded twice. With a live federation visualizer.
Suốt mười phần, “bundle” nghĩa là một app build chung. Module Federation phá vỡ giả định đó: các app riêng biệt, build và triển khai độc lập, có thể import code của nhau lúc runtime. Đây là động cơ phía sau micro-frontend hiện đại.
Bấm các module được expose bên dưới để tải remote vào host, và bật/tắt shared để xem vì sao React không tải hai lần:
1. Vấn đề nó giải quyết
Một app lớn chia cho nhiều team: cart, profile, search. Không có federation, mọi thay đổi ở bất kỳ phần nào buộc build lại và deploy lại cả app. Với federation, mỗi team ship phần của mình độc lập và shell ghép chúng lại trong trình duyệt.
| Thuật ngữ | Nghĩa |
|---|---|
| host | app shell tiêu thụ module remote |
| remote | app expose module cho người khác |
| exposes | remote cung cấp gì |
| remotes | host muốn tiêu thụ gì |
| shared | phụ thuộc tải một lần và dùng lại |
2. Bên remote
Remote khai báo tên, tên file entry, và những gì nó expose:
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "cart",
filename: "remoteEntry.js", // the manifest other apps fetch
exposes: {
"./Cart": "./src/Cart",
"./MiniCart": "./src/MiniCart",
},
shared: { react: { singleton: true } },
}),
],
};
remoteEntry.js là một manifest nhỏ — bản đồ những gì được expose và cách lấy chúng. Chunk Cart thực tế chỉ tải khi có người import nó.
3. Bên host
Host liệt kê remote nào nó muốn và tìm remoteEntry.js của chúng ở đâu:
new ModuleFederationPlugin({
name: "shell",
remotes: {
cart: "cart@https://cart.example.com/remoteEntry.js",
profile: "profile@https://profile.example.com/remoteEntry.js",
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
});
Rồi bạn import module remote như thể nó là local — nhưng nó phân giải qua mạng lúc runtime:
const Cart = React.lazy(() => import("cart/Cart"));
URL thậm chí có thể trỏ tới một remote chưa tồn tại khi host được build. Deploy remote, refresh, xong.
4. shared — tính năng sát thủ
Không có shared, host và mỗi remote sẽ tự bundle React riêng — ba bản, hook hỏng, lãng phí byte. shared biến React thành singleton: tải một lần, dùng lại khắp nơi.
shared: {
react: { singleton: true, requiredVersion: "^18.0.0" },
"react-dom": { singleton: true, requiredVersion: "^18.0.0" },
}
- chỉ một bản tồn tại (bắt buộc cho React, vốn hỏng với hai bản).
- Webpack thương lượng một phiên bản tương thích giữa các app lúc runtime.
- tải trong chunk đầu thay vì async (dùng tiết kiệm).
Bật/tắt shared trong visualizer: khi bật, “react copies loaded” luôn là 1 dù bạn tải bao nhiêu remote.
5. Cảnh báo thực tế
- Lệch phiên bản — nếu remote cần phiên bản major không tương thích, thương lượng singleton có thể cảnh báo hoặc hỏng.
- Lỗi runtime — remote sập là lỗi runtime, không phải lỗi build; bọc trong error boundary và fallback.
- An toàn kiểu — import remote không có kiểu local; sinh
.d.tstừ remote. - Đừng tách quá nhỏ — federation thêm phức tạp vận hành; dùng cho ranh giới team thực sự.
6. Bài tập
1. Host import cart/Cart, nhưng cart còn chưa được deploy khi host build. Vì sao vẫn chạy?
Lời giải
Federation phân giải remote lúc runtime qua remoteEntry.js lấy qua mạng, không phải lúc build.
2. Ba app federated đều dùng React. Làm sao đảm bảo chỉ một bản tải và hook không hỏng?
Lời giải
shared: { react: { singleton: true } } trong config của mọi app.
3. Server remote Cart sập. Tác động tới người dùng và cách kiểm soát?
Lời giải
Đó là lỗi tải runtime — bọc remote lazy trong error boundary với UI fallback để phần còn lại của shell vẫn chạy.
Nâng cao:trong visualizer, tắt shared, tải cả bốn module remote, và để ý “react copies loaded” và KB chuyển tăng so với khi shared.
Điểm chính
- Module Federation cho các app deploy độc lập import lẫn nhau lúc runtime.
- remote
exposesmodule quaremoteEntry.js; host liệt kê trongremotes. shared+singletonngăn bản trùng của React và bạn bè.- Lỗi remote là lỗi runtime — dùng error boundary và fallback.
- Dùng ở ranh giới team thực sự, không để tách app nhỏ.
Tiếp theo
Phần 12 — Capstone production + di trú: ghép tất cả thành một config production thực chiến, rồi hướng dẫn thực tế khi nào (và cách) di trú sang Vite hoặc Rspack.