let apps = [];
export function registerMicroApps(appConfigs) {
apps = appConfigs;
}
export function start() {
window.addEventListener('popstate', handleRouterChange);
handleRouterChange();
}
export function getApps() {
return apps;
}
export async function handleRouterChange() {
const apps = getApps();
const app = apps.find(item => window.location.pathname.startsWith(item.activeRule));
if (!app) return;
const proxySandbox = new ProxySandbox();
proxySandbox.active();
const { htmlTemplate, getExternalScripts, getExternalStylesheet, callScripts } = await importHtml(app.entry);
const appExports = await callScripts();
app.bootstrap = appExports.bootstrap;
app.mount = appExports.mount;
app.unmount = appExports.unmount;
const styleSheets = await getExternalStylesheet();
app.styleSheets = styleSheets;
bootstrapApp(app);
mountApp(app, proxySandbox);
}
async function bootstrapApp(app) {
if (app.bootstrap) {
await app.bootstrap();
}
}
async function mountApp(app, proxySandbox) {
const shadowRootContainer = document.querySelector(app.container) || document.getElementById(app.container);
const shadowRoot = shadowRootContainer.attachShadow({ mode: 'open' });
const microDocument = await createShadowDocument(app);
shadowRoot.appendChild(microDocument);
proxySandbox.active();
if (app.mount) {
await app.mount({ container: shadowRoot });
}
}
async function createShadowDocument(app) {
const microDocument = document.createElement('html');
const container = document.createElement('div');
app.mount && await app.mount({ container });
app.styleSheets.forEach(cssCode => {
const styleAttr = document.createElement('style');
styleAttr.textContent = cssCode;
microDocument.appendChild(styleAttr);
});
microDocument.appendChild(container);
return microDocument;
}
class ProxySandbox {
proxyWindow;
sandboxRunning;
constructor() {
const rawWindow = window;
const fakeWindow = {};
const proxyWindow = new Proxy(fakeWindow, {
set: (target, prop, value) => {
if (this.sandboxRunning) {
target[prop] = value;
return true;
} else {
return false;
}
},
get: (target, prop) => {
const value = prop in target ? target[prop] : rawWindow[prop];
return value;
}
});
this.sandboxRunning = false;
this.proxyWindow = proxyWindow;
}
active() {
this.sandboxRunning = true;
}
inactive() {
this.sandboxRunning = false;
}
}
async function importHtml(url) {
const html = await fetch(url).then(res => res.text());
const htmlTemplate = document.createElement('div');
htmlTemplate.innerHTML = html;
function getExternalScripts() {
const scripts = [];
return Promise.all(scripts);
}
function getExternalStylesheet() {
const styles = [];
return Promise.all(styles);
}
function callScripts() {
const scriptsCode = [];
const module = { exports: {} };
const exports = module.exports;
scriptsCode.forEach(code => {
eval(code);
});
return exports;
}
return {
htmlTemplate,
getExternalScripts,
getExternalStylesheet,
callScripts,
};
}
export default {
registerMicroApps,
start,
getApps
};
import { registerMicroApps, start } from 'mini-qiankun';
registerMicroApps([
{
name: 'react app1',
entry: '//localhost:8001',
container: '#app1',
activeRule: '/qiankun/app1',
},
{
name: 'vue app2',
entry: '//localhost:8002',
container: '#app2',
activeRule: '/qiankun/app2',
},
]);
start();
function App(){
return
<>
<div id='app1' class="scale"></div>
<div id='app2' class="scale"></div>
</>
}