Last active
August 18, 2025 07:42
-
-
Save mzbac/e0526c49f4279f9bd06305cf7480be11 to your computer and use it in GitHub Desktop.
Revisions
-
mzbac revised this gist
Aug 18, 2025 . 1 changed file with 163 additions and 68 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -168,9 +168,10 @@ </header> <nav> <button onclick="loadMFE('dashboard')" id="btn-dashboard">Dashboard MFE (Has Error)</button> <button onclick="loadMFE('profile')" id="btn-profile">Profile MFE</button> <button onclick="loadMFE('settings')" id="btn-settings">Settings MFE (Random Error)</button> <button onclick="loadMFE('broken')" id="btn-broken">Broken MFE</button> <button onclick="unmountCurrent()">Unmount Current</button> </nav> @@ -188,41 +189,27 @@ const mfeModules = { dashboard: { mount: (element) => { // SIMULATE ERROR: This MFE has a bug! try { // Intentional error to demonstrate error handling const someData = null; const result = someData.property; // This will throw! element.innerHTML = ` <div class="mfe-wrapper"> <h2>📊 Dashboard Micro Frontend</h2> <div class="dashboard-widget"> <h3>Sales Overview</h3> <p>Monthly Revenue: $45,230</p> <p>Growth: +12.5%</p> </div> </div> `; } catch (error) { // MFE internal error - re-throw to be caught by container throw new Error(`Dashboard MFE Error: ${error.message}`); } return () => { console.log('Dashboard MFE unmounted'); element.innerHTML = ''; @@ -257,36 +244,65 @@ settings: { mount: (element) => { // Simulate async operation that might fail setTimeout(() => { // Random error simulation (20% chance) if (Math.random() < 0.2) { const error = new Error('Failed to load user settings from API'); console.error('Settings MFE Error:', error); element.innerHTML = ` <div class="mfe-wrapper"> <h2>⚙️ Settings Micro Frontend</h2> <div class="error"> <strong>Settings Error:</strong> Failed to load user settings from API <br><br> <button onclick="loadMFE('settings')">Retry</button> </div> </div> `; return; } // Normal rendering element.innerHTML = ` <div class="mfe-wrapper"> <h2>⚙️ Settings Micro Frontend</h2> <div class="settings-form"> <div class="form-group"> <label>Language</label> <select> <option>English</option> <option>Spanish</option> <option>French</option> </select> </div> <div class="form-group"> <label>Theme</label> <select> <option>Light</option> <option>Dark</option> <option>Auto</option> </select> </div> <div class="form-group"> <label>Notifications</label> <input type="checkbox" id="notifications" checked> <label for="notifications" style="display: inline; margin-left: 8px;"> Enable email notifications </label> </div> <br> <button onclick="alert('Settings MFE: Settings saved!')">Save Settings</button> </div> </div> `; }, 500); // Initial loading state element.innerHTML = ` <div class="mfe-wrapper"> <h2>⚙️ Settings Micro Frontend</h2> <div class="loading">Loading settings...</div> </div> `; @@ -297,6 +313,11 @@ element.innerHTML = ''; }; } }, // Broken MFE - simulates a completely failed module broken: { mount: undefined // This MFE is completely broken! } }; @@ -331,32 +352,106 @@ // Unmount current MFE if exists if (currentUnmount) { try { currentUnmount(); } catch (unmountError) { console.error('Error during unmount:', unmountError); // Continue anyway - don't let unmount errors prevent loading new MFE } currentUnmount = null; } // Dynamically import the MFE module const mfeModule = await importMFE(name); // Validate the module has required mount function if (!mfeModule || typeof mfeModule.mount !== 'function') { throw new Error(`Invalid MFE module: missing mount function`); } // Clear container container.innerHTML = ''; // Create an error boundary wrapper const mfeWrapper = document.createElement('div'); mfeWrapper.id = `mfe-${name}-wrapper`; container.appendChild(mfeWrapper); try { // Mount the MFE with error handling currentUnmount = mfeModule.mount(mfeWrapper); currentMFE = name; console.log(`Container: Successfully loaded and mounted "${name}" MFE`); } catch (mountError) { console.error(`Error mounting ${name} MFE:`, mountError); // Show error UI but keep the container app running container.innerHTML = ` <div class="error"> <h3>⚠️ Micro Frontend Error</h3> <p><strong>${name}</strong> MFE failed to mount</p> <p>Error: ${mountError.message}</p> <br> <button onclick="loadMFE('${name}')">🔄 Retry</button> <button onclick="loadMFE('profile')">Go to Profile</button> </div> `; // Log to monitoring service in production logError({ mfe: name, error: mountError.message, stack: mountError.stack, timestamp: new Date().toISOString() }); } } catch (error) { console.error('Failed to load MFE:', error); // Show user-friendly error with recovery options container.innerHTML = ` <div class="error"> <h3>⚠️ Failed to Load Micro Frontend</h3> <p>The <strong>${name}</strong> module could not be loaded.</p> <p><small>Error: ${error.message}</small></p> <br> <div style="display: flex; gap: 10px;"> <button onclick="loadMFE('${name}')">🔄 Try Again</button> <button onclick="loadMFE('profile')">Load Profile Instead</button> <button onclick="reportIssue('${name}', '${error.message}')">📧 Report Issue</button> </div> </div> `; // Remove active state from failed MFE button document.getElementById(`btn-${name}`)?.classList.remove('active'); } } // Error logging function (would send to monitoring service) function logError(errorData) { console.group('🚨 MFE Error Report'); console.error('MFE:', errorData.mfe); console.error('Message:', errorData.error); console.error('Time:', errorData.timestamp); console.error('Stack:', errorData.stack); console.groupEnd(); // In production, send to error tracking service: // fetch('/api/errors', { // method: 'POST', // body: JSON.stringify(errorData) // }); } // Mock function to report issues function reportIssue(mfeName, errorMessage) { alert(`Issue reported for ${mfeName} MFE:\n${errorMessage}\n\nSupport team has been notified.`); } function unmountCurrent() { if (currentUnmount) { currentUnmount(); -
mzbac created this gist
Aug 18, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,404 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Micro Frontend Demo</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); overflow: hidden; } header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; text-align: center; } h1 { margin: 0; font-size: 24px; } .subtitle { opacity: 0.9; font-size: 14px; margin-top: 5px; } nav { display: flex; gap: 10px; padding: 20px; background: #fafafa; border-bottom: 1px solid #e0e0e0; } button { padding: 10px 20px; border: none; border-radius: 6px; background: #667eea; color: white; cursor: pointer; font-size: 14px; transition: all 0.3s ease; } button:hover { background: #5a67d8; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); } button:active { transform: translateY(0); } button.active { background: #764ba2; } #mfe-container { padding: 20px; min-height: 300px; } .loading { text-align: center; padding: 40px; color: #666; } .error { background: #fee; color: #c00; padding: 20px; border-radius: 6px; margin: 20px; } /* Styles for MFEs */ .mfe-wrapper { padding: 20px; background: #f9f9f9; border-radius: 8px; animation: fadeIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .dashboard-widget { background: white; padding: 20px; border-radius: 8px; margin-bottom: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .profile-card { background: white; padding: 30px; border-radius: 8px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .avatar { width: 100px; height: 100px; border-radius: 50%; background: linear-gradient(135deg, #667eea, #764ba2); margin: 0 auto 20px; display: flex; align-items: center; justify-content: center; color: white; font-size: 36px; } .settings-form { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .form-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: 500; color: #333; } input, select { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } </style> </head> <body> <div class="container"> <header> <h1>Micro Frontend Container App</h1> <div class="subtitle">Dynamic Import Demo with Mount Functions</div> </header> <nav> <button onclick="loadMFE('dashboard')" id="btn-dashboard">Dashboard MFE</button> <button onclick="loadMFE('profile')" id="btn-profile">Profile MFE</button> <button onclick="loadMFE('settings')" id="btn-settings">Settings MFE</button> <button onclick="unmountCurrent()">Unmount Current</button> </nav> <div id="mfe-container"> <div class="loading">Select a micro frontend to load</div> </div> </div> <script> // Container App Code let currentMFE = null; let currentUnmount = null; // Simulated MFE modules (in real app, these would be separate files/bundles) const mfeModules = { dashboard: { mount: (element) => { element.innerHTML = ` <div class="mfe-wrapper"> <h2>📊 Dashboard Micro Frontend</h2> <div class="dashboard-widget"> <h3>Sales Overview</h3> <p>Monthly Revenue: $45,230</p> <p>Growth: +12.5%</p> </div> <div class="dashboard-widget"> <h3>User Activity</h3> <p>Active Users: 1,234</p> <p>New Signups: 89</p> </div> <div class="dashboard-widget"> <h3>System Status</h3> <p>All systems operational ✅</p> </div> </div> `; // Simulate some interactive behavior const widgets = element.querySelectorAll('.dashboard-widget'); widgets.forEach(widget => { widget.style.cursor = 'pointer'; widget.addEventListener('click', () => { widget.style.background = '#f0f4ff'; setTimeout(() => { widget.style.background = 'white'; }, 200); }); }); console.log('Dashboard MFE mounted'); // Return unmount function return () => { console.log('Dashboard MFE unmounted'); element.innerHTML = ''; }; } }, profile: { mount: (element) => { element.innerHTML = ` <div class="mfe-wrapper"> <h2>👤 Profile Micro Frontend</h2> <div class="profile-card"> <div class="avatar">JD</div> <h3>John Doe</h3> <p>[email protected]</p> <p>Member since: January 2024</p> <br> <button onclick="alert('Profile MFE: Edit clicked!')">Edit Profile</button> </div> </div> `; console.log('Profile MFE mounted'); return () => { console.log('Profile MFE unmounted'); element.innerHTML = ''; }; } }, settings: { mount: (element) => { element.innerHTML = ` <div class="mfe-wrapper"> <h2>⚙️ Settings Micro Frontend</h2> <div class="settings-form"> <div class="form-group"> <label>Language</label> <select> <option>English</option> <option>Spanish</option> <option>French</option> </select> </div> <div class="form-group"> <label>Theme</label> <select> <option>Light</option> <option>Dark</option> <option>Auto</option> </select> </div> <div class="form-group"> <label>Notifications</label> <input type="checkbox" id="notifications" checked> <label for="notifications" style="display: inline; margin-left: 8px;"> Enable email notifications </label> </div> <br> <button onclick="alert('Settings MFE: Settings saved!')">Save Settings</button> </div> </div> `; console.log('Settings MFE mounted'); return () => { console.log('Settings MFE unmounted'); element.innerHTML = ''; }; } } }; // Dynamic import simulation (in real app, use actual dynamic imports) async function importMFE(name) { // Simulate network delay await new Promise(resolve => setTimeout(resolve, 300)); // In real implementation, this would be: // const module = await import(`./mfe/${name}/index.js`); // return module; if (!mfeModules[name]) { throw new Error(`MFE "${name}" not found`); } return mfeModules[name]; } async function loadMFE(name) { const container = document.getElementById('mfe-container'); // Update active button document.querySelectorAll('nav button').forEach(btn => { btn.classList.remove('active'); }); document.getElementById(`btn-${name}`)?.classList.add('active'); try { // Show loading state container.innerHTML = '<div class="loading">Loading micro frontend...</div>'; // Unmount current MFE if exists if (currentUnmount) { currentUnmount(); currentUnmount = null; } // Dynamically import the MFE module const mfeModule = await importMFE(name); // Clear container container.innerHTML = ''; // Mount the MFE by passing the container element currentUnmount = mfeModule.mount(container); currentMFE = name; console.log(`Container: Loaded and mounted "${name}" MFE`); } catch (error) { console.error('Failed to load MFE:', error); container.innerHTML = ` <div class="error"> Failed to load micro frontend: ${error.message} </div> `; } } function unmountCurrent() { if (currentUnmount) { currentUnmount(); currentUnmount = null; currentMFE = null; document.getElementById('mfe-container').innerHTML = '<div class="loading">Select a micro frontend to load</div>'; // Remove active state from buttons document.querySelectorAll('nav button').forEach(btn => { btn.classList.remove('active'); }); console.log('Container: Unmounted current MFE'); } } // Example of how this would work with real dynamic imports: /* // Real implementation in separate files: // mfe/dashboard/index.js export function mount(element) { // Dashboard MFE implementation const root = document.createElement('div'); element.appendChild(root); // ... render dashboard return () => { // Cleanup element.removeChild(root); }; } // container/app.js async function loadMFE(name) { const module = await import(`./mfe/${name}/index.js`); const unmount = module.mount(containerElement); // Store unmount function for later cleanup } */ </script> </body> </html>