Micro‑Frontend Architecture: Browser Sandbox, Shadow DOM, and Proxy Techniques
This article explains the concept of micro‑frontend, compares common implementation approaches such as dynamic module loading and browser sandboxing, details the use of Shadow DOM and Proxy for isolation, introduces frameworks like qiankun and hot‑chocolate, and describes an application platform that streamlines build, upload, and release workflows for complex front‑end projects.
Introduction
Front‑end technologies evolve rapidly; frameworks like React, Vue, and UI libraries such as Ant Design receive frequent major updates that are often not backward compatible. When upgrading a back‑office application from Ant Design v2.x to v4.x, massive code changes are required, prompting the need for a smoother migration strategy.
What Is Micro‑Frontend?
Micro‑frontend adopts the micro‑service idea for the front‑end, splitting an application into multiple sub‑applications managed by a host application . Sub‑applications can run independently yet be assembled into a single‑page application.
Key concepts:
Sub‑application: A cohesive set of business pages.
Host application: The base SPA that composes multiple sub‑applications.
The technical focus is on two aspects: (1) ensuring sub‑applications run smoothly via browser sandbox techniques, and (2) designing an application platform for managing and publishing sub‑applications.
Common Solutions for Large Front‑end Apps
Dynamic module loading (import()) : e.g., Webpack lazy load, Module Federation in Webpack 5. Requires a unified tech stack and CSS namespace to avoid conflicts.
Browser sandbox : Uses proxy, global object snapshots, etc., to isolate code. Offers better compatibility but increases implementation complexity.
The sandbox approach shines when needing to run two versions of a library (e.g., Ant Design v2 and v4) simultaneously, though it introduces challenges such as increased bundle size and component reuse.
Popular Micro‑Frontend Frameworks
qiankun : Early framework supporting snapshot sandboxing and exploring Shadow DOM isolation. Requires lifecycle injection and does not currently allow sandbox extension.
hot‑chocolate : Internal framework based on Shadow DOM + Proxy sandboxing, offering near‑zero code intrusion, multi‑app support, and custom plugin injection, but has limited compatibility with older browsers.
Shadow DOM + Proxy Sandbox
Style Isolation with Shadow DOM
What Is Shadow DOM?
Shadow DOM encapsulates markup, styles, and behavior, preventing interference with the rest of the page. It is ideal for style isolation.
Capabilities
Elements inside a Shadow DOM never affect external elements.
Shadow DOM can have a complete DOM subtree.
External styles can be linked without affecting the global scope.
Limitations
JavaScript execution is not isolated; a separate JS sandbox is required.
Key APIs
The main APIs are:
Element.attachShadow({ mode, delegatesFocus }) – attaches a Shadow DOM to an element and returns a ShadowRoot .
mode – "open" or "closed", controlling JavaScript access.
delegatesFocus – when true, focus is delegated to the first focusable element inside the shadow tree.
ShadowRoot – the root of the shadow subtree.
Using innerHTML
Directly setting shadowRoot.innerHTML does not create <head> or <body> nodes. A workaround is to create a document fragment and append constructed elements:
const fragment = document.createDocumentFragment();
const htmlNode = document.createElement('html');
const headNode = document.createElement('head');
const bodyNode = document.createElement('body');
htmlNode.appendChild(headNode);
htmlNode.appendChild(bodyNode);
fragment.appendChild(htmlNode);
shadowRoot.appendChild(fragment);JS Isolation with Proxy
After generating the HTML/CSS via Shadow DOM, the next step is to sandbox JavaScript using with and Proxy .
Using with
The with statement changes the scope of code execution to a specific object, e.g.:
var o = { a: 1, b: 2 };
with (o) {
console.log(a); // 1
b = 3;
}
console.log(o); // {a:1,b:3}Replacing o with a fake window object enables controlled execution.
const fakeWindow = { window: fakeWindow, document, ... };
with (fakeWindow) {
// your code runs here
}Note: with is prohibited in ES5 strict mode and should be used cautiously.
Proxy Basics
A Proxy defines custom behavior for fundamental operations (property access, assignment, etc.). Example:
const p = new Proxy(target, handler);Key handler traps include get , set , and has . A sample proxy that validates an age property:
const person = new Proxy({}, {
has(obj, prop) { return prop in obj || prop === 'age'; },
get(obj, prop) { let v = obj[prop]; if (prop === 'age' && v === undefined) v = 0; return v; },
set(obj, prop, value) {
if (prop === 'age') {
if (typeof value !== 'number') throw new TypeError('Age must be a number');
if (value < 0) throw new RangeError('Age cannot be negative');
}
obj[prop] = value;
return true;
}
});
person.age = 'aaa'; // throws TypeError
person.age = -1; // throws RangeError
console.log(person.age); // 0In the micro‑frontend sandbox, a fakeGlobal proxy intercepts accesses to special globals (e.g., window , document , setTimeout ) and delegates to the original window when necessary.
const fakeGlobal = new Proxy({}, {
has() { return true; },
get(obj, prop) {
// custom handling for document, xxx, etc.
let value;
if (prop === 'document') { /* ... */ }
if (value === undefined && prop in obj) value = obj[prop];
return value;
},
set(obj, prop, value) { fakeWindow[prop] = value; return true; }
});Application Platform for Build and Release
Why Build a Platform?
Managing dozens of sub‑applications manually (e.g., editing JSON config files with names, hashes, etc.) is error‑prone. A dedicated configuration platform centralizes static resource hosting (object storage + CDN) and versioned HTML + hash deployment.
Platform Functions
Static resource hosting via object storage and CDN.
Configuration updates using HTML files with hash‑based cache busting and version control.
Updated Front‑end Workflow
Sub‑application Upload
Using the non‑intrusive hot‑chocolate approach, any bundler (Webpack, Vite, Rollup, etc.) can be used. After the build finishes, the output directory is uploaded unchanged, and the platform loads it via htmlRemote + htmlRoot .
Release Process
The platform treats each environment (test, production) as a separate host application. Developers select the appropriate host and sub‑application versions, then publish instantly. All versions are recorded for easy rollback.
Develop as usual with any bundler.
After development, upload the build output to the platform.
In the platform UI, bind the host and sub‑applications to a version and publish.
This preserves the original development and build flow while adding a lightweight upload and release step, requiring almost no code changes in sub‑applications.
Conclusion
The article covered key micro‑frontend techniques—browser sandboxing, Shadow DOM, Proxy, and a supporting application platform—providing a practical solution for large‑scale front‑end projects. Readers can assess their own needs and decide how to adopt these methods.
LOFTER Tech Team
Technical sharing and discussion from NetEase LOFTER Tech Team
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.