算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
microfrontend【设计与创意】Use this skill when the user asks about microfrontend, Module Federation, Web Components, iframe integration, shared dependencies, or cross-app communication between frontend applications.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Design microfrontend architectures with integration strategy, shared dependency management, and cross-app communication protocols.
User request includes: microfrontend, micro-frontend, module federation, mf, mfe, frontend composition, federated module, webpack federation, rspack federation.
A markdown document containing:
Produce the artifact directly. No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick. If monolith SPA is appropriate, output Monolith SPA recommended. Reason: [reason]. and stop.
——
4096 tokens
Number of independent teams?
|-- 1-2 teams -->
| |-- Monolith SPA recommended (microfrontend overhead not justified)
| |-- Alternative: monorepo with package boundaries
|
|-- 3+ teams on same product -->
| Same framework across all teams?
| |-- YES -->
| | Module Federation (best sharing, smallest total bundle)
| | Build tool? Webpack 5 / Rspack / Vite (with federation plugin)
| |
| |-- NO -->
| Strict isolation needed? (different auth, sandboxed, legacy app)
| |-- YES: iframe (complete isolation, poor perf/SEO)
| |-- NO: Web Components (framework-agnostic, Shadow DOM)
What is the boundary?
|-- Feature / domain (orders, products, users) -->
| GOOD -- team-aligned, independent deploy, clear ownership
|
|-- Page / route (home, checkout, settings) -->
| GOOD -- natural routing boundary, can be lazy-loaded
|
|-- Single component (button, card, modal) -->
| BAD -- too fine-grained, federation overhead > benefit
| Instead: shared component library via federation or package
What type of library?
|-- Framework (React, Vue, Angular, ReactDOM) -->
| MUST be singleton. Two instances break hooks/context/events.
|
|-- State management (Redux, Zustand, Pinia) -->
| Singleton if shared state. Standalone if isolated per MFE.
|
|-- Utility library (lodash, date-fns, axios) -->
| Shared with version range (same version reused, fallback to standalone).
|
|-- Component library (design system) -->
Singleton (shared as federated module for visual consistency).
Determine number of teams, current architecture, frameworks, build tools, and deployment platform.
Choose between Module Federation, Web Components, iframe, or monolith SPA using the selection decision tree.
Specify singleton, shared, or standalone approach per dependency. React/ReactDOM must be singleton.
Select methods: URL-based for navigation, shared state for auth, custom events for domain events, shared event bus for notifications.
Set up host and remote configurations with appropriate exposes, remotes, and shared dependencies.
Each microfrontend gets independent CI/CD with versioned remoteEntry.js and CDN deployment.
Phase extraction: identify boundaries, extract shell, configure federation, extract features one by one, replace monolith.
| Pattern | Isolation | Performance | SEO | Bundle Size |
|---|---|---|---|---|
| Module Federation | Runtime sharing | Good (shared deps) | Requires SSR | Shared deps, smaller total |
| iframe | Complete | Poor (overhead) | Poor | Independent |
| Web Components | Shadow DOM | Good | Good | Framework runtime per component |
| Composition API (Build-time) | Compile-time | Excellent | Excellent | Monolith bundle |
Rule: Microfrontend overhead makes sense at 3+ independent teams working on the same product. Below that, monolith SPA or monorepo with package boundaries is sufficient.
// host/webpack.config.js
new ModuleFederationPlugin({
name: 'shell',
remotes: {
orders: 'orders@https://orders.app.com/remoteEntry.js',
products: 'products@https://products.app.com/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0', eager: true },
'react-dom': { singleton: true, requiredVersion: '^18.2.0', eager: true },
'react-router-dom': { singleton: true },
},
});
// orders-app/webpack.config.js
new ModuleFederationPlugin({
name: 'orders',
filename: 'remoteEntry.js',
exposes: {
'./OrderList': './src/components/OrderList.tsx',
'./OrderDetail': './src/components/OrderDetail.tsx',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
});
// Dynamic remote loader
const OrderList = React.lazy(() => import('orders/OrderList'));
function App() {
return (
<Suspense fallback={<Loading />}>
<OrderList />
</Suspense>
);
}
| Strategy | Behavior | When |
|---|---|---|
| Singleton | Single instance across apps | Framework libs (React, Vue), state management |
| Shared (version-range) | Same version used; fallback if mismatched | Utility libs (lodash, date-fns) |
| Standalone | Each app bundles its own copy | Small libs, version-mismatch-prone libs |
Critical rule: react and react-dom MUST be singleton. Two React instances on the same page break hooks, context, and event system.
// App A dispatches
window.dispatchEvent(new CustomEvent('order-selected', { detail: { orderId: '123' } }));
// App B listens
window.addEventListener('order-selected', (e) => {
navigateToOrder(e.detail.orderId);
});
// shared/auth-store.js (consumed by all apps via federation)
const store = createStore(authReducer);
export { store };
Orders app sets URL /orders/123, Products app reads URL param via router.
| Communication Type | Method |
|---|---|
| Navigation / routing | URL-based |
| Auth state / user info | Shared state store |
| Domain events (order created, item added) | Custom events |
| Toast / notifications | Shared event bus |
@module-federation/enhanced for Rspack support@module-federation/nextjs-mf for Next.js@module-federation/remix@originjs/vite-plugin-federation for Vite-based MFE@angular-architects/module-federation pluginEach microfrontend has its own CI/CD:
remoteEntry.js cache bustingorders-app/
dist/
1.2.3/
remoteEntry.js?ts=1715000000
vendor.js
main.js
Rule: Always version remoteEntry.js with content hash or timestamp. Never share remoteEntry.js directly — use CDN with long cache + versioned paths.
| Strategy | Description | When |
|---|---|---|
| Eager | remoteEntry.js loaded on app init | Always-needed features (auth, nav) |
| Lazy | remoteEntry.js loaded on route change | Route-scoped features |
| Preload | remoteEntry.js loaded after idle | Predicted next navigation |
With 3 MFEs each using React (45KB gzipped):
// Rspack Module Federation (faster builds than Webpack 5)
// rspack.config.js (host)
const { ModuleFederationPlugin } = require('@module-federation/enhanced/rspack');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
orders: 'orders@https://orders.app.com/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
}),
],
};
// Vite Module Federation (@originjs/vite-plugin-federation)
// vite.config.ts (host)
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
federation({
name: 'shell',
remotes: {
orders: 'https://orders.app.com/assets/remoteEntry.js',
},
shared: ['react', 'react-dom'],
}),
],
});
// Microfrontend routing coordination
// Shell manages top-level routes, delegates sub-routes to MFEs
// Shell router (React Router)
const router = createBrowserRouter([
{
path: '/',
element: <Shell />,
children: [
{ index: true, element: <Home /> },
// Delegated to Orders MFE
{ path: 'orders/*', element: <OrdersShell /> },
// Delegated to Products MFE
{ path: 'products/*', element: <ProductsShell /> },
],
},
]);
// Orders MFE receives base path from shell
function OrdersShell() {
return (
<BrowserRouter basename="/orders">
<Routes>
<Route index element={<OrderList />} />
<Route path=":id" element={<OrderDetail />} />
</Routes>
</BrowserRouter>
);
}
// URL-based communication:
// Orders MFE navigates to /products/123 — shell router handles the transition
// No shared state needed for navigation between MFEs
// Auth token stored in httpOnly cookie (accessible to all MFEs via same domain)
// Shell MFE manages login/logout, shares auth state via custom events
// Shell auth service
class AuthService {
private currentUser: User | null = null;
async login(credentials: Credentials): Promise<void> {
const { token, user } = await api.login(credentials);
// Set httpOnly cookie (server-side)
document.cookie = `session=${token}; path=/; HttpOnly; Secure; SameSite=Lax`;
this.currentUser = user;
window.dispatchEvent(new CustomEvent('auth:changed', { detail: { user } }));
}
async logout(): Promise<void> {
document.cookie = 'session=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT';
this.currentUser = null;
window.dispatchEvent(new CustomEvent('auth:changed', { detail: { user: null } }));
}
}
// Any MFE listens:
window.addEventListener('auth:changed', (e: CustomEvent) => {
if (e.detail.user) {
// User logged in — update UI
} else {
// User logged out — redirect to login
}
});
// Dynamic remote loading — useful for feature flags or A/B testing
// Instead of static remotes in webpack config, load at runtime
interface RemoteConfig {
name: string;
url: string;
}
class DynamicFederationLoader {
private loaded = new Map<string, any>();
async loadRemote(config: RemoteConfig): Promise<any> {
if (this.loaded.has(config.name)) {
return this.loaded.get(config.name);
}
// Dynamic import of remote entry
await __webpack_init_sharing__('default');
const container = await import(/* webpackIgnore:true */ config.url);
await container.init(__webpack_share_scopes__.default);
this.loaded.set(config.name, container);
return container;
}
async getModule<T>(remoteName: string, modulePath: string): Promise<() => T> {
const container = await this.loaded.get(remoteName);
const factory = await container.get(modulePath);
return factory();
}
}
// Usage:
// const OrderList = React.lazy(() => dynamicLoader.getModule('orders', './OrderList'));
// Integration testing MFEs together
// Playwright test across MFEs on the same page
test('navigation between MFEs works', async ({ page }) => {
await page.goto('/');
// Click order link in Shell → Orders MFE loads
await page.click('[data-mfe="shell"] a[href="/orders"]');
await expect(page.locator('[data-mfe="orders"]')).toBeVisible();
// Click product link → Products MFE loads
await page.click('[data-mfe="orders"] a[href="/products/123"]');
await expect(page.locator('[data-mfe="products"]')).toBeVisible();
});
// Unit testing Module Federation components
// Mock the remote import
jest.mock('orders/OrderList', () => ({
__esModule: true,
default: () => <div data-testid="mock-order-list">Mocked Orders</div>,
}));
| Technique | Impact | Implementation |
|---|---|---|
| Preload critical remotes | Remove waterfall loading | <link rel="modulepreload" href="orders/remoteEntry.js"> |
| Lazy load non-critical MFEs | Faster initial load | Dynamic import in route component |
| Share large deps as singleton | Reduce duplicate bytes | React, ReactDOM, lodash as singleton |
| Async chunk splitting | Parallel loading | Each MFE produces independent chunks |
| Remote entry caching | Cache remoteEntry.js with hash | Versioned URLs, long cache headers |
| Preconnect to MFE origins | Faster DNS/SSL | <link rel="preconnect" href="https://orders.app.com"> |
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Too fine-grained | Every component is a microfrontend | Group by team ownership and deployment boundaries |
| Shared DB | Tight coupling between frontends | Each MFE owns its data domain |
| No shared design system | Visual inconsistency | Shared component library via federation |
| Circular shared deps | Build-time errors | Strict dependency graph enforcement |
| Wrong version of React | Hooks broken, context lost | Force singleton via Module Federation config |
| Synchronous remote loading | Blocks main thread on remote load | Lazy load all remotes via React.lazy + Suspense |
| Missing error boundaries | One MFE crash takes down whole app | Each MFE wrapped in error boundary |
| No loading states | User sees blank screen on slow network | Suspense fallback per MFE boundary |
| Over-communication | Tight coupling via shared state | Prefer URL-based communication, limit shared state to auth only |
Hand off to frontend/universal/patterns/SKILL.md for component patterns used within each microfrontend. Hand off to frontend/universal/design-system/SKILL.md for shared design system.