从零打造微前端框架:实战“汽车资讯平台”项⽬
从零打造微前端框架:实战“汽车资讯平台”项⽬ - 为什么开发微前端框架
微前端架构具备以下⼏个核⼼价值:
技术栈⽆关 主框架不限制接⼊应⽤的技术栈,⼦应⽤具备完全⾃主权
独⽴开发、独⽴部署 ⼦应⽤仓库独⽴,前后端可独⽴开发,部署完成后主框架⾃动完成同步更新
独⽴运⾏时 每个⼦应⽤之间状态隔离,运⾏时状态不共享
微前端架构旨在解决单体应⽤在⼀个相对长的时间跨度下,由于参与的⼈员、团队的增多、变迁,从⼀个普通应⽤演变成⼀个巨⽯应⽤(Frontend Monolith)后,随之⽽来的应⽤不可维护的问题。这类问题在企业级 Web 应⽤中尤其常见。
资料分享:
从零打造微前端框架:实战“汽车资讯平台”项⽬ - 现有微前端解决⽅案
1、iframe
众所周知,iframe是html提供的标签,能加载其他web应⽤的内容,并且它能兼容所有的浏览器,因此,你可以⽤它来加载任何你想要加载的web应⽤。
iframe最⼤的特性就是提供了浏览器原⽣的硬隔离⽅案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。读到这时,相信⼩伙伴们跟我⼀样,觉得iframe与微前端概念中提到的独⽴开发、独⽴维护、相互隔离⾮常的吻合,有种直接上ifame就完事⼉了的想法,但为何它到现在也不是微前端主要的实现⽅式呢,后来有幸拜读了qiankun技术圆桌中⼀篇关于微前端Why Not Iframe的思考,总结的很到位,现复述其中的⼀段描述
2、Web Components
它的三项主要技术是指:
北京车辆违章查询Custom elements(⾃定义元素):⼀组JavaScript API,允许您定义custom elements及其⾏为,然后可以在您的⽤户界⾯中按照需要使⽤它们。
Shadow DOM(影⼦DOM):⼀组JavaScript API,⽤于将封装的“影⼦”DOM树附加到元素(与主⽂档DOM分开呈现)并控制其关联的功能。通过这种⽅式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,⽽不⽤担⼼与⽂档的其他部分发⽣冲突。HTML templates(HTML模板)
3、ESM
ESM是ES Module的缩写,是Ecma script 2015中提出的⼀种前端模块化⼿段,那么,它⼜是如何做到微前端的呢?其实,微前端⽆外乎三⼤特性,⽆技术栈限制、应⽤单独开发,多应⽤整合,只要抓住了这三个特性,那就不难理解ESM如何做的了:
⽆技术栈限制:ESM加载的只是js内容,⽆论哪个框架,最终都要编译成js,因此,⽆论哪种框架,ESM都能加载。
应⽤单独开发: ESM只是js的⼀种规范,不会影响应⽤的开发模式。
多应⽤整合: 只要将微应⽤以ESM的⽅式暴露出来,就能正常加载。
远程加载模块: ESM能够直接请求cdn资源,这是它与⽣俱来的能⼒。
4、qiankun
在微前端界,qiankun算得上是最早成型且知名度最⼴的框架了,它是真正意义上的单页微前端框架,那么qiankun到底有哪些特点呢,在其官⽹中我到了如下概括:
基于single-spa封装,提供了更加开箱即⽤的 API
北汽b40汽车之家技术栈⽆关,任意技术栈的应⽤均可 使⽤/接⼊,不论是 React/Vue/Angular/JQuery 还是其他等框架
HTML Entry 接⼊⽅式,让你接⼊微应⽤像使⽤ iframe ⼀样简单
样式隔离,确保微应⽤之间样式互相不⼲扰
JS 沙箱,确保微应⽤之间 全局变量/事件 不冲突
资源预加载,在浏览器空闲时间预加载未打开的微应⽤资源,加速微应⽤打开速度
umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应⽤⼀键切换成微前端架构系统
从零打造微前端框架:实战“汽车资讯平台”项⽬ - qiankun微前端框架开发实战
修改主程序main.js注册⼦应⽤
import App from "./App.vue"
import router from "./router"
import { registerMicroApps, setDefaultMountApp, start } from "qiankun"
let app = null;
/**
* 渲染函数
* appContent ⼦应⽤html内容
* loading ⼦应⽤加载效果,可选
*/
function render({ appContent, loading } = {}) {
if (!app) {
app = new Vue({
el: "#app",
router,
data() {
return {
content: appContent,
loading
};
},
render(h) {
return h(App, {
props: {
content: t,
loading: this.loading
}
});
}
});
fjcruiser} else {
app.loading = loading;
}
}
/**
* 路由监听
* @param {*} routerPrefix 前缀
*/
function genActiveRule(routerPrefix) {转向节
return location => location.pathname.startsWith(routerPrefix);
}
function initApp() {
render({ appContent: '', loading: true });
}
initApp();
// 传⼊⼦应⽤的数据
let msg = {
data: {
auth: false
},
fns: [
{
name: "_LOGIN",
_LOGIN(data) {
console.log(`⽗应⽤返回信息${data}`);
}
}
]
]
};
// 注册⼦应⽤
registerMicroApps(
[
{
name: "app2",
entry: "//localhost:8091",
render,
activeRule: genActiveRule("/app2"),
props: msg
},
{
name: "app1",
盐城交巡警违章查询entry: "//localhost:8092",
render,
activeRule: genActiveRule("/app1"),
props: msg
}
],
{
beforeLoad: [
app => {
console.log("before load", app);
}
], // 挂载前回调
beforeMount: [
app => {
console.log("before mount", app);
}
], // 挂载后回调
afterUnmount: [
app => {
console.log("after unload", app);
}
] // 卸载后回调
}
);
// 设置默认⼦应⽤,与 genActiveRule中的参数保持⼀致setDefaultMountApp("/app1");
// 启动
start();
>620