Project Scaffolding Setup
Webpack-Based Micro Frontend Scaffolding
Basic Configuration
// webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: 'auto',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
},
shared: {
react: { singleton: true, requiredVersion: '^17.0.2' },
'react-dom': { singleton: true, requiredVersion: '^17.0.2' },
},
}),
],
devServer: {
port: 3001,
hot: true,
},
};
Advanced Configuration
// webpack.config.js (Advanced Configuration)
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// ...Basic configuration
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
new ModuleFederationPlugin({
// ...Shared configuration
}),
],
};
Vite-Based Micro Frontend Scaffolding
Basic Configuration
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['react', 'react-dom'],
},
},
},
},
});
Remote Application Configuration
// vite.config.js (Remote Application)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 3001,
},
build: {
rollupOptions: {
output: {
entryFileNames: '[name].[hash].js',
chunkFileNames: '[name].[hash].js',
assetFileNames: '[name].[hash].[ext]',
},
},
},
});
Multi-Package Management Tool Configuration
Lerna + Yarn Workspaces
# Project Structure
micro-frontend/
├── packages/
│ ├── app1/
│ ├── app2/
│ └── shared/
├── lerna.json
└── package.json
lerna.json
{
"packages": ["packages/*"],
"version": "independent",
"npmClient": "yarn",
"useWorkspaces": true
}
package.json
{
"private": true,
"workspaces": ["packages/*"],
"devDependencies": {
"lerna": "^6.0.0"
}
}
PNPM Workspaces
package.json
{
"private": true,
"workspaces": ["packages/*"],
"devDependencies": {
"pnpm": "^7.0.0"
}
}
.npmrc
child-concurrency=10
reporter=silent
Dependency Management Strategies
Shared Dependency Extraction
Webpack Shared Configuration
new ModuleFederationPlugin({
// ...
shared: {
react: {
singleton: true,
requiredVersion: '^17.0.2',
eager: true, // Preload
},
'react-dom': {
singleton: true,
requiredVersion: '^17.0.2',
},
},
});
Vite Shared Configuration
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
optimizeDeps: {
include: ['react', 'react-dom'],
},
});
Dependency Version Conflict Resolution
Version Alignment Strategies
- Strict Version Alignment: All applications use the exact same dependency versions.
- Compatible Version Range: Define compatible version ranges.
- Isolated Loading: Create separate contexts for different versions.
// webpack.config.js
new ModuleFederationPlugin({
shared: {
lodash: {
requiredVersion: '^4.17.21',
singleton: false, // Allow multiple versions
},
},
});
Dependency Resolution Priority
- Main Application Priority: Shared dependencies defined by the main application take precedence.
- Latest Compatible Version: Select the latest version compatible with all applications.
- Manual Override: Force a specific version through configuration.
Dependency Loading Optimization
Preloading Strategy
// webpack.config.js
new ModuleFederationPlugin({
// ...
shared: {
react: {
singleton: true,
eager: true, // Preload
},
},
});
On-Demand Loading
// Dynamic import
import('app1/Button').then(({ default: Button }) => {
// Use Button component
});
Code Splitting
// webpack.config.js
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
Build Optimization
Code Splitting and Lazy Loading
Webpack Implementation
// Dynamic import
const Button = React.lazy(() => import('app1/Button'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Button />
</Suspense>
);
}
Vite Implementation
// Dynamic import
const Button = () => import('app1/Button');
function App() {
const [Component, setComponent] = useState(null);
useEffect(() => {
Button().then(module => {
setComponent(() => module.default);
});
}, []);
return Component ? <Component /> : <div>Loading...</div>;
}
Tree Shaking Configuration
Webpack Configuration
// webpack.config.js
module.exports = {
optimization: {
usedExports: true,
minimize: true,
},
};
Babel Configuration
// .babelrc
{
"presets": [
["@babel/preset-env", { "modules": false }],
"@babel/preset-react"
]
}
Long-Term Caching Strategy
File Name Hashing
// webpack.config.js
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
},
Content Hashing
// webpack.config.js
output: {
filename: '[name].[contenthash:8].js',
},
Cache Group Configuration
// webpack.config.js
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: -10,
},
},
},
},
Build Performance Optimization
Parallel Building
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
module.exports = {
optimization: {
minimizer: [
new ParallelUglifyPlugin({
uglifyJS: {
output: {
comments: false,
},
compress: {
warnings: false,
},
},
}),
],
},
};
Cache Optimization
// webpack.config.js
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
};
DLL Precompilation
// webpack.dll.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
vendor: ['react', 'react-dom', 'lodash'],
},
output: {
path: path.join(__dirname, 'dll'),
filename: '[name].dll.js',
library: '[name]_[hash]',
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'dll', '[name]-manifest.json'),
name: '[name]_[hash]',
}),
],
};
Testing Strategies
Unit Testing Framework Selection
Jest Configuration
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
transform: {
'^.+\\.(js|jsx)$': 'babel-jest',
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
};
React Testing Library Example
// Button.test.js
import { render, screen } from '@testing-library/react';
import Button from './Button';
test('renders button with text', () => {
render(<Button>Click me</Button>);
const buttonElement = screen.getByText(/click me/i);
expect(buttonElement).toBeInTheDocument();
});
Integration Testing Solutions
Cypress Integration Testing
// cypress/integration/app_spec.js
describe('App Integration', () => {
it('loads the app', () => {
cy.visit('/');
cy.contains('Welcome to Micro Frontend').should('be.visible');
});
});
Micro Frontend-Specific Integration Testing
// cypress/integration/microfrontend_spec.js
describe('Microfrontend Integration', () => {
it('loads remote app', () => {
cy.visit('/');
cy.get('#remote-app-container').should('exist');
cy.get('#remote-app-container').contains('Remote App Content').should('be.visible');
});
});
E2E Testing Solutions
Playwright Configuration
// playwright.config.js
module.exports = {
use: {
headless: true,
viewport: { width: 1280, height: 720 },
video: 'retain-on-failure',
},
projects: [
{ name: 'Chromium', use: { browserName: 'chromium' } },
{ name: 'Firefox', use: { browserName: 'firefox' } },
{ name: 'WebKit', use: { browserName: 'webkit' } },
],
};
Test Example
// e2e/specs/app.spec.js
const { test, expect } = require('@playwright/test');
test('loads the app', async ({ page }) => {
await page.goto('http://localhost:3000');
await expect(page.locator('text=Welcome')).toBeVisible();
});
Micro Frontend-Specific Test Scenarios
Loading State Testing
// Test remote application loading
cy.intercept('GET', 'http://localhost:3001/remoteEntry.js').as('loadRemote');
cy.visit('/');
cy.wait('@loadRemote');
cy.get('#remote-app').should('be.visible');
Communication Testing
// Test main and sub-application communication
cy.window().then((win) => {
const event = new CustomEvent('test-event', { detail: 'test' });
win.dispatchEvent(event);
});
cy.get('#event-handler').should('contain', 'test');
Sandbox Isolation Testing
// Test style isolation
cy.visit('/');
cy.get('#app1').should('have.css', 'color', 'rgb(255, 0, 0)'); // Red
cy.get('#app2').should('have.css', 'color', 'rgb(0, 0, 255)'); // Blue
Monitoring and Error Tracking
Performance Monitoring Metrics
Core Metrics
- FP (First Paint): Time to first paint.
- FCP (First Contentful Paint): Time to first contentful paint.
- LCP (Largest Contentful Paint): Time to largest contentful paint.
- FID (First Input Delay): Delay for first input.
- CLS (Cumulative Layout Shift): Cumulative layout shift.
Web Vitals Integration
// Import Web Vitals library
import { getCLS, getFID, getLCP } from 'web-vitals';
// Send metrics to analytics service
getCLS(console.log);
getFID(console.log);
getLCP(console.log);
Error Capture and Reporting
Global Error Handling
// Capture global JavaScript errors
window.addEventListener('error', (event) => {
logErrorToService(event.error, event.filename, event.lineno);
});
// Capture unhandled Promise rejections
window.addEventListener('unhandledrejection', (event) => {
logErrorToService(event.reason);
});
function logErrorToService(error, file, line) {
// Report to Sentry or other error monitoring service
Sentry.captureException(error, {
extra: { file, line },
});
}
React Error Boundaries
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo.componentStack);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Use error boundary
<ErrorBoundary>
<App />
</ErrorBoundary>
User Behavior Analysis
Basic Implementation
// Track page views
window.addEventListener('load', () => {
trackPageView(window.location.pathname);
});
// Track click events
document.addEventListener('click', (event) => {
trackEvent('click', {
target: event.target.tagName,
href: event.target.href,
});
});
function trackPageView(path) {
// Send to analytics service
analytics.track('page_view', { path });
}
function trackEvent(name, data) {
analytics.track(name, data);
}
Micro Frontend-Specific Analysis
// Track micro frontend loading
window.addEventListener('microfrontend-loaded', (event) => {
trackEvent('microfrontend_load', {
name: event.detail.name,
loadTime: event.detail.loadTime,
});
});
// Trigger after micro frontend loads
window.dispatchEvent(new CustomEvent('microfrontend-loaded', {
detail: {
name: 'app1',
loadTime: performance.now() - startTime,
},
}));
Performance Analysis
// Record performance metrics
const metrics = {
tbt: performance.metrics.totalBlockingTime,
fid: performance.getEntriesByName('first-input')[0]?.processingStart - performance.now(),
cls: performance.metrics.layoutShift.total,
};
trackPerformance(metrics);
Advanced Monitoring Solutions
Custom Metrics
// Define custom metrics
const customMetric = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
trackCustomMetric(entry.name, entry.value);
}
});
customMetric.observe({ type: 'custom', buffered: true });
Distributed Tracing
// Generate trace ID
const traceId = generateTraceId();
// Pass trace ID in requests
fetch('/api/data', {
headers: {
'X-Trace-ID': traceId,
},
});
// Record trace information
trackTrace(traceId, {
startTime: performance.now(),
duration: performance.now() - startTime,
});
Real-Time Monitoring
// WebSocket for real-time data sending
const socket = new WebSocket('wss://monitoring.example.com');
socket.onopen = () => {
setInterval(() => {
const metrics = collectCurrentMetrics();
socket.send(JSON.stringify(metrics));
}, 5000);
};
Monitoring System Integration
Sentry Integration
import * as Sentry from '@sentry/react';
import { Integrations } from '@sentry/tracing';
Sentry.init({
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
integrations: [new Integrations.BrowserTracing()],
tracesSampleRate: 1.0,
});
// Use in React components
function MyComponent() {
return (
<Sentry.ErrorBoundary fallback={<p>An error occurred</p>}>
<MyChildComponent />
</Sentry.ErrorBoundary>
);
}
Datadog Integration
import { datadogRum } from '@datadog/browser-rum';
datadogRum.init({
applicationId: 'YOUR_APPLICATION_ID',
clientToken: 'YOUR_CLIENT_TOKEN',
site: 'datadoghq.com',
service: 'micro-frontend-app',
env: 'production',
version: '1.0.0',
sampleRate: 100,
trackInteractions: true,
});
// Record custom event
datadogRum.addEvent({
name: 'custom_event',
service: 'micro-frontend',
type: 'custom',
tags: { feature: 'navigation' },
});
Monitoring Data Visualization
Grafana Dashboard
// Grafana Dashboard JSON Example
{
"panels": [
{
"type": "graph",
"title": "FP Metrics",
"targets": [
{
"expr": "rate(microfrontend_fp_seconds_sum[1m])",
"legendFormat": "{{app}}"
}
]
},
{
"type": "table",
"title": "Error Rates",
"targets": [
{
"expr": "sum(rate(microfrontend_errors_total[1m])) by (app)",
"legendFormat": "{{app}}"
}
]
}
]
}
Kibana Visualization
// Kibana Visualization Configuration
{
"title": "Microfrontend Performance",
"visState": {
"type": "line",
"params": {
"type": "line",
"series": [
{
"id": 1,
"data": {
"id": "1",
"label": "FCP",
"color": "#7EB26D"
},
"valueAxes": "1",
"drawLinesBetweenPoints": true,
"showCircles": true
}
],
"addTooltip": true,
"addLegend": true,
"scale": "linear",
"drawLinesBetweenPoints": true,
"interpolate": "linear",
"mode": "stacked"
},
"aggs": [
{
"id": "1",
"enabled": true,
"type": "avg",
"schema": "metric",
"params": {
"field": "FCP"
}
},
{
"id": "2",
"enabled": true,
"type": "terms",
"schema": "segment",
"params": {
"field": "app.keyword",
"size": 5,
"order": "desc",
"orderBy": "_term"
}
},
{
"id": "3",
"enabled": true,
"type": "date_histogram",
"schema": "bucket",
"params": {
"field": "@timestamp",
"interval": "auto",
"min_doc_count": 1,
"extended_bounds": {}
}
}
]
}
}
Summary
Micro frontend engineering practices encompass multiple aspects, from project scaffolding setup to dependency management, build optimization, testing strategies, and monitoring with error tracking. Each aspect requires careful design and optimization to ensure high performance, maintainability, and stability of micro frontend applications.
- Project Scaffolding Setup: Select appropriate toolchains (Webpack or Vite), configure multi-package management tools (Lerna or PNPM), and establish a unified development environment.
- Dependency Management Strategies: Extract and share dependencies effectively, resolve version conflicts, optimize dependency loading strategies, and ensure compatibility across sub-applications.
- Build Optimization: Implement code splitting and lazy loading, configure Tree Shaking, establish long-term caching strategies, improve build performance, and reduce build times.
- Testing Strategies: Choose appropriate unit testing frameworks (e.g., Jest), implement integration and E2E testing, cover micro frontend-specific test scenarios, and ensure application quality.
- Monitoring and Error Tracking: Define key performance metrics, capture and report errors, analyze user behavior, integrate monitoring systems (e.g., Sentry, Datadog), and enable real-time monitoring and visualization.
Through systematic engineering practices, micro frontend architecture can fully leverage its advantages, enabling modular development, independent deployment, and efficient collaboration for large-scale frontend applications.



