共计 3498 个字符,预计需要花费 9 分钟才能阅读完成。
实现微前端的十种方式【二】
- 实现微前端,我想了一想,大概有十种方式
- 想学习微前端的小伙伴,可以看我之前对微前端源码解析、加载方式、以及我开源的微前端框架 chunchao 源码
- 简单的文章,通俗易懂,感觉不错记得点个
在看
和关注
哦
目前主流的微前端实现方式(基座加载式)
- 以基座为入口,配置不同的子应用入口地址,达到实现微前端的效果
- 目前微前端开源的框架:
chunchao
、qiankun
, 其中chunchao
仅仅 200 行代码就实现了,是一个非常值得定制开发的微前端雏形框架 - 微前端基座模式配置
- 加载示意图
如何实现基座模式加载子应用?
- 劫持前端路由, 重写
hashchange
和popstate
事件
const HIJACK_EVENTS_NAME = /^(hashchange|popstate)$/i;
const EVENTS_POOL = {hashchange: [],
popstate: [],};
window.addEventListener('hashchange', loadApps);
window.addEventListener('popstate', loadApps);
const originalAddEventListener = window.addEventListener;
const originalRemoveEventListener = window.removeEventListener;
window.addEventListener = function (eventName, handler) {
if (
eventName &&
HIJACK_EVENTS_NAME.test(eventName) &&
typeof handler === 'function'
) {EVENTS_POOL[eventName].indexOf(handler) === -1 &&
EVENTS_POOL[eventName].push(handler);
}
return originalAddEventListener.apply(this, arguments);
};
- 根据不同的入口,去拉取子应用的
js
、css
等资源
- 注册子应用后存入队列中
/**
*
* @param {string} entry
* @param {string} function
*/
const Apps = [] // 子应用队列
function registryApp(entry,activeRule) {
Apps.push({
entry,
activeRule
})
}
- 注册完了之后,就要找到需要加载的 app, 并且拉取资源
export async function loadApp() {const shouldMountApp = Apps.filter(shouldBeActive);
const App = shouldMountApp.pop();
fetch(App.entry)
.then(function (response) {return response.text();
})
.then(async function (text) {const dom = document.createElement('div');
dom.innerHTML = text;
const entryPath = App.entry;
const scripts = dom.querySelectorAll('script');
const subapp = document.querySelector('#subApp-content');
const paromiseArr =
scripts &&
Array.from(scripts).map((item) => {if (item.src) {
const url = window.location.protocol + '//' + window.location.host;
return fetch(`${entryPath}/${item.src}`.replace(url, '')).then(function (response) {return response.text();
}
);
} else {return Promise.resolve(item.textContent);
}
});
subapp.appendChild(dom);
const res = await Promise.all(paromiseArr);
if (res && res.length > 0) {res.forEach((item) => {const script = document.createElement('script');
script.innerText = item;
subapp.appendChild(script);
});
}
});
}
- shouldBeActive 根据传入的规则去判断是否需要此时挂载:
export function shouldBeActive(app){return app.activeRule(window.location)
}
- 处理脚本文件
export async function handleScripts(entryPath,subapp,dom) {const scripts = dom.querySelectorAll('script');
const paromiseArr =
scripts &&
Array.from(scripts).map((item) => {if (item.src) {
const url = window.location.protocol + '//' + window.location.host;
return fetch(`${entryPath}/${item.src}`.replace(url, '')).then(function (response) {return response.text();
}
);
} else {return Promise.resolve(item.textContent);
}
});
const res = await Promise.all(paromiseArr);
if (res && res.length > 0) {res.forEach((item) => {const script = document.createElement('script');
script.innerText = item;
subapp.appendChild(script);
});
}
}
- 处理样式文件
export async function handleStyles(entryPath, subapp, dom) {const arr = [];
const styles = dom.querySelectorAll('style');
const links = Array.from(dom.querySelectorAll('link')).filter((item) => item.rel === 'stylesheet'
);
const realArr = arr.concat(styles,links)
const paromiseArr =
arr &&
Array.from(realArr).map((item) => {if (item.rel) {
const url = window.location.protocol + '//' + window.location.host;
return fetch(`${entryPath}/${item.href}`.replace(url, '')).then(function (response) {return response.text();
}
);
} else {return Promise.resolve(item.textContent);
}
});
const res = await Promise.all(paromiseArr);
if (res && res.length > 0) {res.forEach((item) => {const style = document.createElement('style');
style.innerHTML = item;
subapp.appendChild(style);
});
}
}
- 此时,我们已经可以加载不同的子应用了。
最后
- 认真收藏这个系列吧, 记得点个关注和在看, 相信你能收获很多很多~
- 我是
Peter
,架构设计过桌面跨平台IM
软件、重型 Saas 平台以及手机端跨平台APP
, 我的微信:CALASFxiaotan - 另外欢迎收藏我的资料网站: 前端生活社区:
https://qianduan.life
, 感觉对你有帮助,可以右下角点个在看
,关注
一波公众号:[前端巅峰
]
正文完