手写一个微前端,qiankunjs
window.__POWERED_BY_QIANKUN__ = true;
let module = null;
export const registerMicroApps = (apps = []) => {
const _pushState = history.pushState;
history.pushState = (...args) => {
_pushState.apply(history, args);
handler(apps);
};
window.addEventListener('popstate', () => handler(apps));
window.addEventListener('load', () => handler(apps));
};
export const start = () => {};
// 处理函数
async function handler(apps) {
const app = findRoute(apps, location.pathname);
// 如果没有匹配的子应用,则不处理
if (!app) {
// 若上一次挂载是子应用,则卸载它
if (module) {
module.exports.unmount();
module.container.innerHTML = '';
module = null;
}
return;
}
// 相同子应用,不处理
if (module?.app?.entry === app.entry) return;
// 切换子应用
const container = document.querySelector(app.container);
// 卸载上一个子应用
if (module) {
module.exports.unmount();
module.container.innerHTML = '';
module = null;
}
container.innerHTML = await parseDom(app.entry);
const codes = await parseScript(container, app.entry);
window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ = app.entry + '/';
module = runCode(codes);
module.container = container;
module.app = app;
module.exports.mount({ container });
}
// 根据url获取dom
function parseDom(url) {
return fetch(url).then(res => res.text());
}
// 根据dom获取scirpt脚本
function parseScript(el, baseUrl) {
const scripts = el.querySelectorAll('script');
return Promise.all(
Array.from(scripts).map(script => {
const src = script.getAttribute('src');
console.log('src :>> ', src);
if (src) return fetch(baseUrl + src).then(res => res.text());
return Promise.resolve(script.innerHTML);
})
);
}
// 执行代码
function runCode(codes) {
// 构造一个commonjs环境,获取子应用暴露出来的钩子函数
let exports = {};
let module = { exports };
for (const code of codes) {
eval(code);
}
return module;
}
// 查找应用
function findRoute(apps, path) {
return apps.find(app => path.startsWith(app.activeRule));
}
仅实现了一些基础的功能,样式隔离,js沙箱,数据共享等还没做。