微前端microApp實踐
微前端的概念是由ThoughtWorks在2016年提出的,它借鑑了微服務的架構理念,核心在於將一個龐大的前端應用拆分成多個獨立靈活的小型應用,每個應用都可以獨立開發、獨立執行、獨立部署,再將這些小型應用融合為一個完整的應用,或者將原本執行已久、沒有關聯的幾個應用融合為一個應用。微前端既可以將多個專案融合為一,又可以減少專案之間的耦合,提升專案擴充套件性,相比一整塊的前端倉庫,微前端架構下的前端倉庫傾向於更小更靈活。
專案改造背景
因專案需要要做一個大資料融合平臺,主要功能模組包括使用者管理、流程管理、大資料治理運算元應用,基礎元件等混合構成的融合平臺;公司目前已經存在使用者管理、流程管理等成品;基本主內容也是和這些產品的功能一致,主框架只是控制著導航和頭部資訊;因此希望能夠抱著複用的方式,更快的進行融合接入。
前端流行的微服務框架比對
引數 | singlespa | qianku | microapp |
---|---|---|---|
開發成本 | 開發成本高 | 開發成本中 | 開發成本高 |
維護成本 | 中 | 中 | 中 |
技術棧 | 不限技術棧 | 不限技術棧 | 不限技術棧 |
實現難易 | 實現難 | 實現中 | 實現簡單 |
原理 | 監聽 url change 事件 | 監聽 url change 事件 | WebComponent |
經過對比,目前覺得micro-app使用簡單,將所有功能都封裝到一個類WebComponent元件中,從而實現在基座應用中嵌入一行程式碼即可渲染一個微前端應用。同時micro-app還提供了js沙箱、樣式隔離、元素隔離、預載入、資料通訊、靜態資源補全等一系列完善的功能。子應用基本不需要過多的進行專案改造,相對使用成本更低
對接說明
- 主框架依賴包引入
import microApp from '@micro-zoe/micro-app'
- 主應用啟動(並可以進行全域性配置)
microApp.start({
plugins: {
global: [{
loader(code, url, options) { // 必填
console.log('全域性外掛')
return code
}
}],
}
}
})
//相關引數
microApp.start({
inline: true, // 預設值false
destroy: true, // 預設值false
disableScopecss: true, // 預設值false
disableSandbox: true, // 預設值false
shadowDOM: true, // 預設值false
ssr: true, // 預設值false
})
- 配置元件節點
<micro-app
style="height: 100%;"
name='appnameUserCenter'
:url='url'
:data='microAppData'
@created='handleCreate'
@beforemount='handleBeforeMount'
@mounted='handleMount'
@unmount='handleUnmount'
@error='handleError'
@datachange='handleDataChange'
></micro-app>
生命週期列表
- created
<micro-app>標籤初始化後,載入資源前觸發。 - beforemount
載入資源完成後,開始渲染之前觸發。 - mounted
子應用渲染結束後觸發。 - unmount
子應用解除安裝時觸發。 - error
子應用渲染出錯時觸發,只有會導致渲染終止的錯誤才會觸發此生命週期。
- 路由匹配設定(模糊匹配,建議主應用history,微應用hash模式,)
{
path: '/mainPanel',
name: 'mainPanel',
component: mainPanel,
children:[
{
path: '/mainPanel/dataCenter*',
name: 'dataCenter',
component:()=>dataCenter
},
{
path: '/mainPanel/userCenter*',
name: 'userCenter',
component:()=>userCenter
},
]
}
- 主微框架通訊
microMenu(appName, path, hash) {
if (!getActiveApps().includes(appName)) {
path = '/mainPanel' + path+'/'
// child-vite 和 child-react17子應用為hash路由,這裡拼接一下hash值
hash && (path += `/#${hash}`)
// 主應用跳轉
this.$router.push(path)
} else {
let childPath = null
// child-vite 和 child-react17子應用是hash路由,hash值就是它的頁面地址,這裡單獨處理
if (hash) {
childPath = hash
} else {
// path的值形式如:/app-vue2/page2,這裡/app-vue2是子應用的基礎路由,/page2才是頁面地址,所以我們需要將/app-vue2部分刪除
childPath = path.replace(/^\/app-[^/]+/, '')
!childPath && (childPath = '/') // 防止地址為空
}
// 主應用通過下發data資料控制子應用跳轉
microApp.setData(appName, { path: childPath })
}
}
- 微框架及其路由跳轉
(function () {
let app = null;
//建立vue例項
const createVue = function () {
app = new Vue({
el: "#app",
router,
store,
render: (h) => h(App),
});
};
// 與基座進行資料互動
const handleMicroData = function (callBack) {
window.microApp.addDataListener((data) => {
callBack(data);
});
};
// 微前端環境下,註冊mount和unmount方法
if (window.__MICRO_APP_ENVIRONMENT__) {
let {token} = window.microApp.getData();
token && setToken(token);
console.log("data-center token:", token);
window[`micro-app-${window.__MICRO_APP_NAME__}`] = {
//微前端掛載
mount: () => {
createVue();
handleMicroData((data) => {
//互動操作
store.dispatch("settings/changeSetting", {
key: "microFlag",
value: true,
});
console.log("data-center addDataListener:", data);
router.push(data.path);
});
},
//微前端解除安裝
unmount: () => {
app.$destroy();
app.$el.innerHTML = "";
app = null;
console.log("微應用child-vue2解除安裝了");
},
};
} else {
// 非微前端環境直接渲染
createVue();
}
})();