Dynamic Module Loading
import() Dynamic Import Syntax
Basic Usage of import():
// Basic dynamic import
import('./module.js')
.then(module => {
module.someFunction();
})
.catch(err => {
console.error('Module loading failed', err);
});
// Using async/await
async function loadModule() {
try {
const module = await import('./module.js');
module.someFunction();
} catch (err) {
console.error('Module loading failed', err);
}
}Dynamic Import Features:
- Returns a Promise: import() returns a Promise object.
- Runtime Resolution: Module paths can be determined at runtime.
- Code Splitting: Tools like Webpack automatically split dynamically imported modules into separate chunks.
- Browser Support: Natively supported by modern browsers and Node.js (ESM).
Dynamic Path Example:
// Conditional dynamic import
const modulePath = isPremiumUser ? './premiumModule.js' : './freeModule.js';
import(modulePath).then(module => {
module.init();
});
// Dynamic import based on user input
function loadPlugin(pluginName) {
import(`./plugins/${pluginName}.js`)
.then(plugin => plugin.init())
.catch(err => console.error(`Plugin ${pluginName} loading failed`, err));
}On-Demand Loading and Code Splitting
On-Demand Loading Patterns:
- Route-Level Code Splitting:
// React Router example
const Home = React.lazy(() => import('./routes/Home'));
const About = React.lazy(() => import('./routes/About'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
);
}- Component-Level Code Splitting:
// Dynamic loading of heavy components
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function MyComponent() {
const [showHeavy, setShowHeavy] = useState(false);
return (
<div>
<button onClick={() => setShowHeavy(true)}>Load Heavy Component</button>
{showHeavy && (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
)}
</div>
);
}- Library-Level Code Splitting:
// On-demand loading of large libraries
async function loadChartLibrary() {
if (window.Chart) return; // Skip if already loaded
const chartLib = await import('chart.js');
window.Chart = chartLib.default;
initCharts();
}
// Call when needed
loadChartLibrary();Code Splitting Strategies:
- Entry Splitting: Multiple independent entry files.
- Common Splitting: Extract common dependencies into separate chunks.
- Dynamic Splitting: Load modules on demand at runtime.
Performance Optimization for Dynamic Module Loading
Optimization Strategies:
- Preloading/Prefetching:
// Webpack magic comments
import(/* webpackPrefetch: true */ './future-module.js');
import(/* webpackPreload: true */ './critical-module.js');
// Manual prefetching
function prefetchModule() {
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = '/path/to/module.js';
link.as = 'script';
document.head.appendChild(link);
}- Smart Loading Timing:
// Predictive loading based on user behavior
document.getElementById('feature-button').addEventListener('mouseover', () => {
import('./feature-module.js');
});
// Loading during network idle time
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
import('./non-critical-module.js');
});
} else {
setTimeout(() => {
import('./non-critical-module.js');
}, 5000);
}- Caching Optimization:
// Cache dynamically loaded modules with Service Worker
self.addEventListener('fetch', event => {
if (event.request.url.includes('/dynamic-modules/')) {
event.respondWith(
caches.match(event.request)
.then(response => response || fetchAndCache(event.request))
);
}
});
async function fetchAndCache(request) {
const cache = await caches.open('dynamic-modules');
const response = await fetch(request);
cache.put(request, response.clone());
return response;
}Application Scenarios for Dynamic Module Loading
Route Lazy Loading:
// Vue Router example
const routes = [
{
path: '/dashboard',
component: () => import(/* webpackChunkName: "dashboard" */ './views/Dashboard.vue')
},
{
path: '/settings',
component: () => import(/* webpackChunkName: "settings" */ './views/Settings.vue')
}
];Plugin System:
// Plugin loader implementation
class PluginManager {
constructor() {
this.plugins = {};
}
async loadPlugin(pluginName) {
if (this.plugins[pluginName]) return this.plugins[pluginName];
try {
const pluginModule = await import(`./plugins/${pluginName}.js`);
const plugin = pluginModule.default;
this.plugins[pluginName] = plugin;
return plugin;
} catch (err) {
console.error(`Plugin ${pluginName} loading failed`, err);
throw err;
}
}
async executePlugin(pluginName, ...args) {
const plugin = await this.loadPlugin(pluginName);
return plugin.execute(...args);
}
}
// Usage example
const pluginManager = new PluginManager();
pluginManager.executePlugin('analytics', { trackPageView: true });On-Demand Loading of Third-Party Libraries:
// On-demand loading of map library
let mapInstance = null;
async function initMap(containerId) {
if (mapInstance) return mapInstance;
const mapLib = await import('leaflet');
const map = mapLib.map(containerId).setView([51.505, -0.09], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
mapInstance = map;
return map;
}Error Handling and Fallback Mechanisms for Dynamic Module Loading
Robust Error Handling:
// Dynamic import with retry mechanism
async function loadModuleWithRetry(modulePath, retries = 3, delay = 1000) {
try {
const module = await import(modulePath);
return module;
} catch (err) {
if (retries <= 0) {
throw new Error(`Module ${modulePath} loading failed, retries exhausted`);
}
await new Promise(resolve => setTimeout(resolve, delay));
return loadModuleWithRetry(modulePath, retries - 1, delay * 2);
}
}
// Usage example
loadModuleWithRetry('./critical-module.js')
.then(module => module.init())
.catch(err => {
console.error('Final loading failed:', err);
loadFallbackModule();
});Fallback Mechanism Implementation:
// Load fallback module if primary module fails
async function loadModuleWithFallback(primaryPath, fallbackPath) {
try {
return await import(primaryPath);
} catch (primaryError) {
console.warn(`Primary module ${primaryPath} loading failed, attempting fallback`, primaryError);
try {
return await import(fallbackPath);
} catch (fallbackError) {
console.error(`Fallback module ${fallbackPath} also failed`, fallbackError);
throw new Error('All module loading attempts failed');
}
}
}
// Usage example
loadModuleWithFallback(
'./modern-module.js',
'./legacy-module.js'
).then(module => {
module.init();
});Graceful Degradation Scheme:
// Feature degradation implementation
async function loadFeature() {
try {
const featureModule = await import('./advanced-feature.js');
featureModule.enableAdvancedFeatures();
} catch (err) {
console.warn('Advanced features unavailable, enabling basic features', err);
enableBasicFeatures();
}
}
function enableBasicFeatures() {
// Implement basic functionality
}Module Federation
Concept and Core Principles of Module Federation
Core Concepts:
- Distributed Module System: Allows different applications to share modules.
- Remote Modules: Expose modules for use by other applications.
- Shared Dependencies: Coordinate dependency versions across applications.
- Runtime Integration: No build-time dependency relationships required.
Architectural Advantages:
- Independent development and deployment.
- Maximized code reuse.
- Progressive upgrade capability.
- Flexible architectural composition.
Webpack Module Federation Configuration and Implementation
Basic Configuration Example:
// App1 (Host Application)
new ModuleFederationPlugin({
name: 'app1',
remotes: {
app2: 'app2@http://localhost:3002/remoteEntry.js'
},
shared: ['react', 'react-dom']
});
// App2 (Remote Application)
new ModuleFederationPlugin({
name: 'app2',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button'
},
shared: ['react', 'react-dom']
});Advanced Configuration:
// More complex shared configuration
shared: {
react: {
singleton: true, // Ensure only one React instance
requiredVersion: '^17.0.0'
},
'react-dom': {
singleton: true,
requiredVersion: '^17.0.0'
},
lodash: {
eager: true // Load immediately instead of on-demand
}
}Dynamic Remote Configuration:
// Dynamically configure remote application at runtime
const loadRemoteEntry = async (url) => {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
};
const initApp = async () => {
try {
await loadRemoteEntry('http://localhost:3002/remoteEntry.js');
const { getModule } = window.app2;
const Button = await getModule('./Button');
// Use Button component
} catch (err) {
console.error('Remote module loading failed', err);
// Load local fallback component
const { LocalButton } = await import('./LocalButton');
// Use LocalButton component
}
};Cross-Application Module Sharing and Communication
Module Sharing Patterns:
- Component Sharing:
// Remote application exposes component
exposes: {
'./UserProfile': './src/components/UserProfile'
}
// Host application uses component
const UserProfile = await import('app2/UserProfile');- Utility Function Sharing:
// Remote application exposes utility functions
exposes: {
'./utils': './src/utils'
}
// Host application uses utility functions
const { formatDate } = await import('app2/utils');- State Management Sharing:
// Shared Redux store
shared: {
'react-redux': {
singleton: true
},
'@reduxjs/toolkit': {
singleton: true
}
}Inter-Application Communication:
- Custom Events:
// Application A sends event
window.dispatchEvent(new CustomEvent('data-updated', {
detail: { data: newData }
}));
// Application B listens for event
window.addEventListener('data-updated', (event) => {
console.log('Received data update:', event.detail.data);
});- Shared State:
// Shared Redux store example
// In shared store configuration
const store = configureStore({
reducer: {
shared: sharedReducer
}
});
// Access same store in different applications
const dispatch = useDispatch();
const sharedData = useSelector(state => state.shared.data);Security and Version Control in Module Federation
Security Measures:
- Content Security Policy (CSP):
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' http://trusted-cdn.com">- Subresource Integrity (SRI):
<script
src="http://localhost:3002/remoteEntry.js"
integrity="sha384-...">
</script>- Sandbox Isolation:
<iframe sandbox="allow-scripts allow-same-origin" src="http://remote-app"></iframe>Version Control Strategies:
- Exact Version Locking:
shared: {
react: {
requiredVersion: '17.0.2',
singleton: true
}
}- Version Range Control:
shared: {
react: {
requiredVersion: '^17.0.0',
singleton: true
}
}- Version Conflict Resolution:
shared: {
react: {
requiredVersion: '^17.0.0',
strictVersion: true // Strict version matching
}
}Application Scenarios for Module Federation
Micro-Frontend Architecture:
// Main application configuration
new ModuleFederationPlugin({
name: 'mainApp',
remotes: {
dashboard: 'dashboard@http://dashboard.example.com/remoteEntry.js',
admin: 'admin@http://admin.example.com/remoteEntry.js'
},
shared: ['react', 'react-dom', 'api']
});
// Dynamic loading of micro-apps
const loadMicroApp = async (appName) => {
const remote = window[appName];
const component = await remote.get('./Widget');
// Render component
};Multi-Team Collaboration:
- Design System Sharing:
// Design system application exposes components
exposes: {
'./Button': './src/api/components/Button',
'./Input': './src/api/Input.js'
}
// Business application uses design system components
const Button = await import('design-system/Button');- Feature Module Sharing:
// Authentication service application exposes functionality
exposes: {
'./AuthService': './src/api/services/AuthService'
}
// Other applications use authentication service
const AuthService = await import('auth-service/AuthService');Modularization in Micro-Apps
Architecture Design for Micro-Frontends
Application Splitting Principles:
- Business Capability-Driven:
- Divide application boundaries by business domain:
- Each micro-app focuses on a specific business function:
- Divide application boundaries by business domain:
- Technology Stack Independence:
- Allow different micro-apps to use different tech stacks:
- Achieve interoperability via Module Federation:
- Clear Data Ownership:
- Each micro-app manages its own data state:
- Communicate via APIs or shared state:
Modularization Practices in Micro-Frontends
Independent Development Workflow:
- Local Development Configuration:
// Development environment Webpack configuration
devServer: {
port: 3001,
headers: {
'Access-Control-Allow-Origin': '*'
}
},
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button'
},
shared: ['react', 'react-dom']
})
]- Independent Build and Deployment:
// package.json
{
"scripts": {
"build": "webpack --config webpack.prod.js",
"deploy": "aws s3 sync dist s3://my-bucket/app1"
}
}Independent Deployment Strategies:
- Blue-Green Deployment:
- Keep old version running:
- Deploy new version to a new environment:
- Switch traffic via DNS:
- Canary Release:
- Gradually shift traffic to the new version:
- Monitor new version performance:
- Quickly roll back if issues arise:
Micro-Frontend Frameworks’ Modularization Support
qiankun Framework Integration:
// Main application configuration
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'app1',
entry: '//localhost:7100',
container: '#app1-container',
activeRule: '/app1',
},
{
name: 'app2',
entry: '//localhost:7101',
container: '#app2-container',
activeRule: '/app2',
}
]);
start();
// Sub-application configuration
if (!window.__POWERED_BY_QIANKUN__) {
// Entry for standalone execution
render();
}
export async function bootstrap() {
console.log('Sub-app bootstrap');
}
export async function mount(props) {
console.log('Sub-app mount', props);
render(props);
}
export async function unmount(props) {
console.log('Sub-app unmount', props);
ReactDOM.unmountComponentAtNode(props.container);
}Single-SPA Configuration:
// Main application configuration
import { registerApplication, start } from 'single-spa';
registerApplication(
'app1',
() => System.import('http://localhost:7100/app1.js'),
location => location.pathname.startsWith('/app1')
);
registerApplication(
'app2',
() => System.import('http://localhost:7101/app2.js'),
location => location.pathname.startsWith('/app2')
);
start();
// Sub-application configuration (SystemJS)
SystemJS.config({
map: {
react: 'https://cdn.jsdelivr.net/npm/react@17.0.2/umd/react.production.min.js',
'react-dom': 'https://cdn.jsdelivr.net/npm/react-dom@17.0.2/umd/react-dom.production.min.js'
}
});
define(['react', 'react-dom'], (react, reactDom) => {
const App = () => <div>Hello from App1</div>;
const mount = (el) => reactDom.render(<App />, el);
const unmount = (el) => reactDom.unmountComponentAtNode(el);
return { mount, unmount };
});Communication Mechanisms in Micro-Frontends
Event Bus Pattern:
// Main application creates event bus
const eventBus = {
events: {},
emit(event, ...args) {
if (!this.events[event]) return;
this.events[event].forEach(cb => cb(...args));
},
on(event, cb) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(cb);
}
};
// Expose to sub-applications
window.microFrontendEventBus = eventBus;
// Sub-application uses event bus
window.microFrontendEventBus.on('user-logged-in', (userData) => {
console.log('User login event:', userData);
updateUI(userData);
});
// Trigger event
window.microFrontendEventBus.emit('cart-updated', { items: 5 });Shared State Pattern:
// Shared Redux store example
// Main application configures store
const store = configureStore({
reducer: {
shared: sharedReducer,
app1: app1Reducer,
app2: app2Reducer
}
});
// Expose store to sub-applications
window.sharedStore = store;
// Sub-application accesses shared state
const dispatch = useDispatch();
const sharedData = useSelector(state => state.shared.data);
// Update shared state
dispatch({ type: 'SHARED_DATA_UPDATE', payload: newData });Custom Communication Protocol:
// Define communication protocol
const protocol = {
commands: {
getUser: {
request: { type: 'GET_USER' },
response: { type: 'USER_DATA', payload: {} }
},
updateCart: {
request: { type: 'UPDATE_CART', payload: {} },
response: { type: 'CART_UPDATED', payload: {} }
}
}
};
// Implement communication service
class MicroFrontendService {
constructor() {
this.handlers = {};
}
registerHandler(command, handler) {
this.handlers[command] = handler;
}
async execute(command, payload) {
if (!this.handlers[command]) {
throw new Error(`Unknown command: ${command}`);
}
return this.handlers[command](payload);
}
}
// Create global service instance
window.microFrontendService = new MicroFrontendService();
// Sub-application registers handler
window.microFrontendService.registerHandler('getUser', async () => {
const response = await fetch('/api/user');
return response.json();
});Performance Optimization and Challenges in Micro-Frontends
Performance Optimization Strategies:
- Preloading Strategy:
// Preload potentially needed micro-apps
function prefetchMicroApps() {
const likelyApps = ['app1', 'app2'];
likelyApps.forEach(appName => {
import(`./micro-apps/${appName}.js`);
});
}
// Preload during user idle time
if ('requestIdleCallback' in window) {
requestIdleCallback(prefetchMicroApps);
}- Resource Caching:
// Service Worker caching strategy
self.addEventListener('fetch', event => {
if (event.request.url.includes('/micro-apps/')) {
event.respondWith(
caches.match(event.request)
.then(response => response || fetchAndCache(event.request))
);
}
});- On-Demand Loading Optimization:
// Precise loading based on route
const loadApp = async (route) => {
switch(route) {
case '/app1':
return import('./micro-apps/app1.js');
case '/app2':
return import('./micro-apps/app2.js');
default:
return Promise.resolve(null);
}
};Main Challenges and Solutions:
- Style Isolation:
- Use Shadow DOM or CSS scoping techniques:
- Implement namespace prefixes:
- JavaScript Sandboxing:
- Use Proxy for JavaScript sandbox isolation:
- Avoid global variable pollution:
- Communication Latency:
- Implement local caching to reduce cross-app calls:
- Use WebSocket for real-time communication:
- Version Compatibility:
- Implement API version control:
- Provide compatibility layers for version differences:
- Debugging Complexity:
- Implement unified logging system:
- Integrate development debugging tools:



