基于 qiankun 的微前端最佳實(shí)踐(萬字長文) - 從 0 到 1 篇
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">引言"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
大家好~
本文是基于 qiankun 的微前端最佳實(shí)踐系列文章之 從 0 到 1 篇,本文將分享如何使用 qiankun 如何搭建主應(yīng)用基座,然后接入不同技術(shù)棧的微應(yīng)用,完成微前端架構(gòu)的從 0 到 1。
本教程采用 Vue 作為主應(yīng)用基座,接入不同技術(shù)棧的微應(yīng)用。如果你不懂 Vue 也沒關(guān)系,我們在搭建主應(yīng)用基座的教程盡量不涉及 Vue 的 API,涉及到 API 的地方都會給出解釋。
注意:
qiankun屬于無侵入性的微前端框架,對主應(yīng)用基座和微應(yīng)用的技術(shù)棧都沒有要求。
我們在本教程中,接入了多技術(shù)棧 微應(yīng)用 的 主應(yīng)用 最終效果圖如下:

"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">構(gòu)建主應(yīng)用基座"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 (案例是以 Vue 為基座的主應(yīng)用,接入多個(gè)微應(yīng)用) 為例,來介紹一下如何在 qiankun 中如何接入不同技術(shù)棧的微應(yīng)用。
我們先使用 vue-cli 生成一個(gè) Vue 的項(xiàng)目,初始化主應(yīng)用。
vue-cli 是
Vue官方提供的腳手架工具,用于快速搭建一個(gè)Vue項(xiàng)目。如果你想跳過這一步,可以直接clone實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 的代碼。
將普通的項(xiàng)目改造成 qiankun 主應(yīng)用基座,需要進(jìn)行三步操作:
創(chuàng)建微應(yīng)用容器 - 用于承載微應(yīng)用,渲染顯示微應(yīng)用; 注冊微應(yīng)用 - 設(shè)置微應(yīng)用激活條件,微應(yīng)用地址等等; 啟動 qiankun;
"display: none;">創(chuàng)建微應(yīng)用容器"display: none;">
我們先在主應(yīng)用中創(chuàng)建微應(yīng)用的承載容器,這個(gè)容器規(guī)定了微應(yīng)用的顯示區(qū)域,微應(yīng)用將在該容器內(nèi)渲染并顯示。
我們先設(shè)置路由,路由文件規(guī)定了主應(yīng)用自身的路由匹配規(guī)則,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/routes/index.ts
"color: #c678dd;line-height: 26px;">import?Home?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"@/pages/home/index.vue";
"color: #c678dd;line-height: 26px;">const?routes?=?[
??{
????"color: #5c6370;font-style: italic;line-height: 26px;">/**
?????*?path:?路徑為?/?時(shí)觸發(fā)該路由規(guī)則
?????*?name:?路由的?name?為?Home
?????*?component:?觸發(fā)路由時(shí)加載?`Home`?組件
?????*/
????path:?"color: #98c379;line-height: 26px;">"/",
????name:?"color: #98c379;line-height: 26px;">"Home",
????component:?Home,
??},
];
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?routes;
"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/main.ts
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #c678dd;line-height: 26px;">import?Vue?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"vue";
"color: #c678dd;line-height: 26px;">import?VueRouter?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"vue-router";
"color: #c678dd;line-height: 26px;">import?routes?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./routes";
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?注冊路由實(shí)例
?*?即將開始監(jiān)聽?location?變化,觸發(fā)路由規(guī)則
?*/
"color: #c678dd;line-height: 26px;">const?router?=?"color: #c678dd;line-height: 26px;">new?VueRouter({
??mode:?"color: #98c379;line-height: 26px;">"history",
??routes,
});
"color: #5c6370;font-style: italic;line-height: 26px;">//?創(chuàng)建?Vue?實(shí)例
"color: #5c6370;font-style: italic;line-height: 26px;">//?該實(shí)例將掛載/渲染在?id?為?main-app?的節(jié)點(diǎn)上
"color: #c678dd;line-height: 26px;">new?Vue({
??router,
??render:?"line-height: 26px;">("line-height: 26px;">h)?=>?h(App),
}).$mount("color: #98c379;line-height: 26px;">"#main-app");
從上面代碼可以看出,我們設(shè)置了主應(yīng)用的路由規(guī)則,設(shè)置了 Home 主頁的路由匹配規(guī)則。
我們現(xiàn)在來設(shè)置主應(yīng)用的布局,我們會有一個(gè)菜單和顯示區(qū)域,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/App.vue
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?"color: #c678dd;line-height: 26px;">class?App?"color: #c678dd;line-height: 26px;">extends?Vue?{
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?菜單列表
???*?key:?唯一?Key?值
???*?title:?菜單標(biāo)題
???*?path:?菜單對應(yīng)的路徑
???*/
??menus?=?[
????{
??????key:?"color: #98c379;line-height: 26px;">"Home",
??????title:?"color: #98c379;line-height: 26px;">"主頁",
??????path:?"color: #98c379;line-height: 26px;">"/",
????},
??];
}
上面的代碼是我們對菜單配置的實(shí)現(xiàn),我們還需要實(shí)現(xiàn)基座和微應(yīng)用的顯示區(qū)域(如下圖)

我們來分析一下上面的代碼:
第 5 行:主應(yīng)用菜單,用于渲染菜單;第 9 行:主應(yīng)用渲染區(qū)。在觸發(fā)主應(yīng)用路由規(guī)則時(shí)(由路由配置表的$route.name判斷),將渲染主應(yīng)用的組件;第 10 行:微應(yīng)用渲染區(qū)。在未觸發(fā)主應(yīng)用路由規(guī)則時(shí)(由路由配置表的$route.name判斷),將渲染微應(yīng)用節(jié)點(diǎn);
從上面的分析可以看出,我們使用了在路由表配置的 name 字段進(jìn)行判斷,判斷當(dāng)前路由是否為主應(yīng)用路由,最后決定渲染主應(yīng)用組件或是微應(yīng)用節(jié)點(diǎn)。
由于篇幅原因,樣式實(shí)現(xiàn)代碼就不貼出來了,最后主應(yīng)用的實(shí)現(xiàn)效果如下圖所示:

從上圖可以看出,我們主應(yīng)用的組件和微應(yīng)用是顯示在同一片內(nèi)容區(qū)域,根據(jù)路由規(guī)則決定渲染規(guī)則。
"display: none;">注冊微應(yīng)用"display: none;">
在構(gòu)建好了主框架后,我們需要使用 qiankun 的 registerMicroApps 方法注冊微應(yīng)用,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/micro/apps.ts
"color: #5c6370;font-style: italic;line-height: 26px;">//?此時(shí)我們還沒有微應(yīng)用,所以?apps?為空
"color: #c678dd;line-height: 26px;">const?apps?=?[];
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?apps;
"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/micro/index.ts
"color: #5c6370;font-style: italic;line-height: 26px;">//?一個(gè)進(jìn)度條插件
"color: #c678dd;line-height: 26px;">import?NProgress?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"nprogress";
"color: #c678dd;line-height: 26px;">import?"color: #98c379;line-height: 26px;">"nprogress/nprogress.css";
"color: #c678dd;line-height: 26px;">import?{?message?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"ant-design-vue";
"color: #c678dd;line-height: 26px;">import?{
??registerMicroApps,
??addGlobalUncaughtErrorHandler,
??start,
}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"qiankun";
"color: #5c6370;font-style: italic;line-height: 26px;">//?微應(yīng)用注冊信息
"color: #c678dd;line-height: 26px;">import?apps?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./apps";
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?注冊微應(yīng)用
?*?第一個(gè)參數(shù)?-?微應(yīng)用的注冊信息
?*?第二個(gè)參數(shù)?-?全局生命周期鉤子
?*/
registerMicroApps(apps,?{
??"color: #5c6370;font-style: italic;line-height: 26px;">//?qiankun?生命周期鉤子?-?微應(yīng)用加載前
??beforeLoad:?"line-height: 26px;">("line-height: 26px;">app:?"color: #e6c07b;line-height: 26px;">any)?=>?{
????"color: #5c6370;font-style: italic;line-height: 26px;">//?加載微應(yīng)用前,加載進(jìn)度條
????NProgress.start();
????"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"before?load",?app.name);
????"color: #c678dd;line-height: 26px;">return?"color: #e6c07b;line-height: 26px;">Promise.resolve();
??},
??"color: #5c6370;font-style: italic;line-height: 26px;">//?qiankun?生命周期鉤子?-?微應(yīng)用掛載后
??afterMount:?"line-height: 26px;">("line-height: 26px;">app:?"color: #e6c07b;line-height: 26px;">any)?=>?{
????"color: #5c6370;font-style: italic;line-height: 26px;">//?加載微應(yīng)用前,進(jìn)度條加載完成
????NProgress.done();
????"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"after?mount",?app.name);
????"color: #c678dd;line-height: 26px;">return?"color: #e6c07b;line-height: 26px;">Promise.resolve();
??},
});
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?添加全局的未捕獲異常處理器
?*/
addGlobalUncaughtErrorHandler("line-height: 26px;">("line-height: 26px;">event:?Event?|?"color: #e6c07b;line-height: 26px;">string)?=>?{
??"color: #e6c07b;line-height: 26px;">console.error(event);
??"color: #c678dd;line-height: 26px;">const?{?message:?msg?}?=?event?"color: #c678dd;line-height: 26px;">as?"color: #e6c07b;line-height: 26px;">any;
??"color: #5c6370;font-style: italic;line-height: 26px;">//?加載失敗時(shí)提示
??"color: #c678dd;line-height: 26px;">if?(msg?&&?msg.includes("color: #98c379;line-height: 26px;">"died?in?status?LOADING_SOURCE_CODE"))?{
????message.error("color: #98c379;line-height: 26px;">"微應(yīng)用加載失敗,請檢查應(yīng)用是否可運(yùn)行");
??}
});
"color: #5c6370;font-style: italic;line-height: 26px;">//?導(dǎo)出?qiankun?的啟動函數(shù)
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?start;
從上面可以看出,我們的微應(yīng)用注冊信息在 apps 數(shù)組中(此時(shí)為空,我們在后面接入微應(yīng)用時(shí)會添加微應(yīng)用注冊信息),然后使用 qiankun 的 registerMicroApps 方法注冊微應(yīng)用,最后導(dǎo)出了 start 函數(shù),注冊微應(yīng)用的工作就完成啦!
"display: none;">啟動主應(yīng)用"display: none;">
我們在注冊好了微應(yīng)用,導(dǎo)出 start 函數(shù)后,我們需要在合適的地方調(diào)用 start 啟動主應(yīng)用。
我們一般是在入口文件啟動 qiankun 主應(yīng)用,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/main.ts
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #c678dd;line-height: 26px;">import?startQiankun?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./micro";
startQiankun();
最后,啟動我們的主應(yīng)用,效果圖如下:

因?yàn)槲覀冞€沒有注冊任何微應(yīng)用,所以這里的效果圖和上面的效果圖是一樣的。
到這一步,我們的主應(yīng)用基座就創(chuàng)建好啦!
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">接入微應(yīng)用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
我們現(xiàn)在的主應(yīng)用基座只有一個(gè)主頁,現(xiàn)在我們需要接入微應(yīng)用。
qiankun 內(nèi)部通過 import-entry-html 加載微應(yīng)用,要求微應(yīng)用需要導(dǎo)出生命周期鉤子函數(shù)(見下圖)。

從上圖可以看出,qiankun 內(nèi)部會校驗(yàn)微應(yīng)用的生命周期鉤子函數(shù),如果微應(yīng)用沒有導(dǎo)出這三個(gè)生命周期鉤子函數(shù),則微應(yīng)用會加載失敗。
如果我們使用了腳手架搭建微應(yīng)用的話,我們可以通過 webpack 配置在入口文件處導(dǎo)出這三個(gè)生命周期鉤子函數(shù)。如果沒有使用腳手架的話,也可以直接在微應(yīng)用的 window 上掛載這三個(gè)生命周期鉤子函數(shù)。
現(xiàn)在我們來接入我們的各個(gè)技術(shù)棧微應(yīng)用吧!
注意,下面的內(nèi)容對相關(guān)技術(shù)棧
API不會再有過多介紹啦,如果你要接入不同技術(shù)棧的微應(yīng)用,最好要對該技術(shù)棧有一些基礎(chǔ)了解。
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">接入 Vue 微應(yīng)用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例,我們在主應(yīng)用的同級目錄(micro-app-main 同級目錄),使用 vue-cli 先創(chuàng)建一個(gè) Vue 的項(xiàng)目,在命令行運(yùn)行如下命令:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">vue?create?micro-app-vue
本文的 vue-cli 選項(xiàng)如下圖所示,你也可以根據(jù)自己的喜好選擇配置。

在新建項(xiàng)目完成后,我們創(chuàng)建幾個(gè)路由頁面再加上一些樣式,最后效果如下:


"display: none;">注冊微應(yīng)用"display: none;">
在創(chuàng)建好了 Vue 微應(yīng)用后,我們可以開始我們的接入工作了。首先我們需要在主應(yīng)用中注冊該微應(yīng)用的信息,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/micro/apps.ts
"color: #c678dd;line-height: 26px;">const?apps?=?[
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?name:?微應(yīng)用名稱?-?具有唯一性
???*?entry:?微應(yīng)用入口?-?通過該地址加載微應(yīng)用
???*?container:?微應(yīng)用掛載節(jié)點(diǎn)?-?微應(yīng)用加載完成后將掛載在該節(jié)點(diǎn)上
???*?activeRule:?微應(yīng)用觸發(fā)的路由規(guī)則?-?觸發(fā)路由規(guī)則后將加載該微應(yīng)用
???*/
??{
????name:?"color: #98c379;line-height: 26px;">"VueMicroApp",
????entry:?"color: #98c379;line-height: 26px;">"http://localhost:10200",
????container:?"color: #98c379;line-height: 26px;">"#frame",
????activeRule:?"color: #98c379;line-height: 26px;">"/vue",
??},
];
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?apps;
通過上面的代碼,我們就在主應(yīng)用中注冊了我們的 Vue 微應(yīng)用,進(jìn)入 /vue 路由時(shí)將加載我們的 Vue 微應(yīng)用。
我們在菜單配置處也加入 Vue 微應(yīng)用的快捷入口,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/App.vue
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?"color: #c678dd;line-height: 26px;">class?App?"color: #c678dd;line-height: 26px;">extends?Vue?{
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?菜單列表
???*?key:?唯一?Key?值
???*?title:?菜單標(biāo)題
???*?path:?菜單對應(yīng)的路徑
???*/
??menus?=?[
????{
??????key:?"color: #98c379;line-height: 26px;">"Home",
??????title:?"color: #98c379;line-height: 26px;">"主頁",
??????path:?"color: #98c379;line-height: 26px;">"/",
????},
????{
??????key:?"color: #98c379;line-height: 26px;">"VueMicroApp",
??????title:?"color: #98c379;line-height: 26px;">"Vue?主頁",
??????path:?"color: #98c379;line-height: 26px;">"/vue",
????},
????{
??????key:?"color: #98c379;line-height: 26px;">"VueMicroAppList",
??????title:?"color: #98c379;line-height: 26px;">"Vue?列表頁",
??????path:?"color: #98c379;line-height: 26px;">"/vue/list",
????},
??];
}
菜單配置完成后,我們的主應(yīng)用基座效果圖如下

"display: none;">配置微應(yīng)用"display: none;">
在主應(yīng)用注冊好了微應(yīng)用后,我們還需要對微應(yīng)用進(jìn)行一系列的配置。首先,我們在 Vue 的入口文件 main.js 中,導(dǎo)出 qiankun 主應(yīng)用所需要的三個(gè)生命周期鉤子函數(shù),代碼實(shí)現(xiàn)如下:

從上圖來分析:
第 6 行:webpack默認(rèn)的publicPath為""空字符串,會基于當(dāng)前路徑來加載資源。我們在主應(yīng)用中加載微應(yīng)用時(shí)需要重新設(shè)置publicPath,這樣才能正確加載微應(yīng)用的相關(guān)資源。(public-path.js具體實(shí)現(xiàn)在后面)第 21 行:微應(yīng)用的掛載函數(shù),在主應(yīng)用中運(yùn)行時(shí)將在mount生命周期鉤子函數(shù)中調(diào)用,可以保證在沙箱內(nèi)運(yùn)行。第 38 行:微應(yīng)用獨(dú)立運(yùn)行時(shí),直接執(zhí)行render函數(shù)掛載微應(yīng)用。第 46 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -bootstrap。第 53 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -mount。第 61 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -unmount。
完整代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-vue/src/public-path.js
"color: #c678dd;line-height: 26px;">if?("color: #e6c07b;line-height: 26px;">window.__POWERED_BY_QIANKUN__)?{
??"color: #5c6370;font-style: italic;line-height: 26px;">//?動態(tài)設(shè)置?webpack?publicPath,防止資源加載出錯
??"color: #5c6370;font-style: italic;line-height: 26px;">//?eslint-disable-next-line?no-undef
??__webpack_public_path__?=?"color: #e6c07b;line-height: 26px;">window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-vue/src/main.js
"color: #c678dd;line-height: 26px;">import?Vue?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"vue";
"color: #c678dd;line-height: 26px;">import?VueRouter?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"vue-router";
"color: #c678dd;line-height: 26px;">import?Antd?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"ant-design-vue";
"color: #c678dd;line-height: 26px;">import?"color: #98c379;line-height: 26px;">"ant-design-vue/dist/antd.css";
"color: #c678dd;line-height: 26px;">import?"color: #98c379;line-height: 26px;">"./public-path";
"color: #c678dd;line-height: 26px;">import?App?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./App.vue";
"color: #c678dd;line-height: 26px;">import?routes?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./routes";
Vue.use(VueRouter);
Vue.use(Antd);
Vue.config.productionTip?=?"color: #56b6c2;line-height: 26px;">false;
"color: #c678dd;line-height: 26px;">let?instance?=?"color: #56b6c2;line-height: 26px;">null;
"color: #c678dd;line-height: 26px;">let?router?=?"color: #56b6c2;line-height: 26px;">null;
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?渲染函數(shù)
?*?兩種情況:主應(yīng)用生命周期鉤子中運(yùn)行?/?微應(yīng)用單獨(dú)啟動時(shí)運(yùn)行
?*/
"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">render("line-height: 26px;">)?{
??"color: #5c6370;font-style: italic;line-height: 26px;">//?在?render?中創(chuàng)建?VueRouter,可以保證在卸載微應(yīng)用時(shí),移除?location?事件監(jiān)聽,防止事件污染
??router?=?"color: #c678dd;line-height: 26px;">new?VueRouter({
????"color: #5c6370;font-style: italic;line-height: 26px;">//?運(yùn)行在主應(yīng)用中時(shí),添加路由命名空間?/vue
????"color: #d19a66;line-height: 26px;">base:?"color: #e6c07b;line-height: 26px;">window.__POWERED_BY_QIANKUN__???"color: #98c379;line-height: 26px;">"/vue"?:?"color: #98c379;line-height: 26px;">"/",
????"color: #d19a66;line-height: 26px;">mode:?"color: #98c379;line-height: 26px;">"history",
????routes,
??});
??"color: #5c6370;font-style: italic;line-height: 26px;">//?掛載應(yīng)用
??instance?=?"color: #c678dd;line-height: 26px;">new?Vue({
????router,
????"color: #d19a66;line-height: 26px;">render:?"line-height: 26px;">("line-height: 26px;">h)?=>?h(App),
??}).$mount("color: #98c379;line-height: 26px;">"#app");
}
"color: #5c6370;font-style: italic;line-height: 26px;">//?獨(dú)立運(yùn)行時(shí),直接掛載應(yīng)用
"color: #c678dd;line-height: 26px;">if?(!"color: #e6c07b;line-height: 26px;">window.__POWERED_BY_QIANKUN__)?{
??render();
}
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?* bootstrap 只會在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會直接調(diào)用 mount 鉤子,不會再重復(fù)觸發(fā) bootstrap。
?*?通常我們可以在這里做一些全局變量的初始化,比如不會在 unmount 階段被銷毀的應(yīng)用級別的緩存等。
?*/
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">async?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">bootstrap("line-height: 26px;">)?{
??"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"VueMicroApp?bootstraped");
}
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?應(yīng)用每次進(jìn)入都會調(diào)用?mount?方法,通常我們在這里觸發(fā)應(yīng)用的渲染方法
?*/
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">async?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">mount("line-height: 26px;">props)?{
??"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"VueMicroApp?mount",?props);
??render(props);
}
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?應(yīng)用每次?切出/卸載?會調(diào)用的方法,通常在這里我們會卸載微應(yīng)用的應(yīng)用實(shí)例
?*/
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">async?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">unmount("line-height: 26px;">)?{
??"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"VueMicroApp?unmount");
??instance.$destroy();
??instance?=?"color: #56b6c2;line-height: 26px;">null;
??router?=?"color: #56b6c2;line-height: 26px;">null;
}
在配置好了入口文件 main.js 后,我們還需要配置 webpack,使 main.js 導(dǎo)出的生命周期鉤子函數(shù)可以被 qiankun 識別獲取。
我們直接配置 vue.config.js 即可,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-vue/vue.config.js
"color: #c678dd;line-height: 26px;">const?path?=?"color: #e6c07b;line-height: 26px;">require("color: #98c379;line-height: 26px;">"path");
"color: #e6c07b;line-height: 26px;">module.exports?=?{
??"color: #d19a66;line-height: 26px;">devServer:?{
????"color: #5c6370;font-style: italic;line-height: 26px;">//?監(jiān)聽端口
????"color: #d19a66;line-height: 26px;">port:?"color: #d19a66;line-height: 26px;">10200,
????"color: #5c6370;font-style: italic;line-height: 26px;">//?關(guān)閉主機(jī)檢查,使微應(yīng)用可以被?fetch
????"color: #d19a66;line-height: 26px;">disableHostCheck:?"color: #56b6c2;line-height: 26px;">true,
????"color: #5c6370;font-style: italic;line-height: 26px;">//?配置跨域請求頭,解決開發(fā)環(huán)境的跨域問題
????"color: #d19a66;line-height: 26px;">headers:?{
??????"color: #98c379;line-height: 26px;">"Access-Control-Allow-Origin":?"color: #98c379;line-height: 26px;">"*",
????},
??},
??"color: #d19a66;line-height: 26px;">configureWebpack:?{
????"color: #d19a66;line-height: 26px;">resolve:?{
??????"color: #d19a66;line-height: 26px;">alias:?{
????????"color: #98c379;line-height: 26px;">"@":?path.resolve(__dirname,?"color: #98c379;line-height: 26px;">"src"),
??????},
????},
????"color: #d19a66;line-height: 26px;">output:?{
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?微應(yīng)用的包名,這里與主應(yīng)用中注冊的微應(yīng)用名稱一致
??????"color: #d19a66;line-height: 26px;">library:?"color: #98c379;line-height: 26px;">"VueMicroApp",
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?將你的?library?暴露為所有的模塊定義下都可運(yùn)行的方式
??????"color: #d19a66;line-height: 26px;">libraryTarget:?"color: #98c379;line-height: 26px;">"umd",
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?按需加載相關(guān),設(shè)置為?webpackJsonp_VueMicroApp?即可
??????"color: #d19a66;line-height: 26px;">jsonpFunction:?"color: #98c379;line-height: 26px;">`webpackJsonp_VueMicroApp`,
????},
??},
};
我們需要重點(diǎn)關(guān)注一下 output 選項(xiàng),當(dāng)我們把 libraryTarget 設(shè)置為 umd 后,我們的 library 就暴露為所有的模塊定義下都可運(yùn)行的方式了,主應(yīng)用就可以獲取到微應(yīng)用的生命周期鉤子函數(shù)了。
在 vue.config.js 修改完成后,我們重新啟動 Vue 微應(yīng)用,然后打開主應(yīng)用基座 http://localhost:9999。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用,此時(shí)我們的 Vue 微應(yīng)用被正確加載啦?。ㄒ娤聢D)

此時(shí)我們打開控制臺,可以看到我們所執(zhí)行的生命周期鉤子函數(shù)(見下圖)

到這里,Vue 微應(yīng)用就接入成功了!
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">接入 React 微應(yīng)用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例,我們在主應(yīng)用的同級目錄(micro-app-main 同級目錄),使用 react-create-app 先創(chuàng)建一個(gè) React 的項(xiàng)目,在命令行運(yùn)行如下命令:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">npx?create-react-app?micro-app-react
在項(xiàng)目創(chuàng)建完成后,我們在根目錄下添加 .env 文件,設(shè)置項(xiàng)目監(jiān)聽的端口,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">#?micro-app-react/.env
PORT=10100
BROWSER=none
然后,我們創(chuàng)建幾個(gè)路由頁面再加上一些樣式,最后效果如下:


"display: none;">注冊微應(yīng)用"display: none;">
在創(chuàng)建好了 React 微應(yīng)用后,我們可以開始我們的接入工作了。首先我們需要在主應(yīng)用中注冊該微應(yīng)用的信息,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/micro/apps.ts
"color: #c678dd;line-height: 26px;">const?apps?=?[
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?name:?微應(yīng)用名稱?-?具有唯一性
???*?entry:?微應(yīng)用入口?-?通過該地址加載微應(yīng)用
???*?container:?微應(yīng)用掛載節(jié)點(diǎn)?-?微應(yīng)用加載完成后將掛載在該節(jié)點(diǎn)上
???*?activeRule:?微應(yīng)用觸發(fā)的路由規(guī)則?-?觸發(fā)路由規(guī)則后將加載該微應(yīng)用
???*/
??{
????name:?"color: #98c379;line-height: 26px;">"ReactMicroApp",
????entry:?"color: #98c379;line-height: 26px;">"http://localhost:10100",
????container:?"color: #98c379;line-height: 26px;">"#frame",
????activeRule:?"color: #98c379;line-height: 26px;">"/react",
??},
];
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?apps;
通過上面的代碼,我們就在主應(yīng)用中注冊了我們的 React 微應(yīng)用,進(jìn)入 /react 路由時(shí)將加載我們的 React 微應(yīng)用。
我們在菜單配置處也加入 React 微應(yīng)用的快捷入口,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/App.vue
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?"color: #c678dd;line-height: 26px;">class?App?"color: #c678dd;line-height: 26px;">extends?Vue?{
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?菜單列表
???*?key:?唯一?Key?值
???*?title:?菜單標(biāo)題
???*?path:?菜單對應(yīng)的路徑
???*/
??menus?=?[
????{
??????key:?"color: #98c379;line-height: 26px;">"Home",
??????title:?"color: #98c379;line-height: 26px;">"主頁",
??????path:?"color: #98c379;line-height: 26px;">"/",
????},
????{
??????key:?"color: #98c379;line-height: 26px;">"ReactMicroApp",
??????title:?"color: #98c379;line-height: 26px;">"React?主頁",
??????path:?"color: #98c379;line-height: 26px;">"/react",
????},
????{
??????key:?"color: #98c379;line-height: 26px;">"ReactMicroAppList",
??????title:?"color: #98c379;line-height: 26px;">"React?列表頁",
??????path:?"color: #98c379;line-height: 26px;">"/react/list",
????},
??];
}
菜單配置完成后,我們的主應(yīng)用基座效果圖如下

"display: none;">配置微應(yīng)用"display: none;">
在主應(yīng)用注冊好了微應(yīng)用后,我們還需要對微應(yīng)用進(jìn)行一系列的配置。首先,我們在 React 的入口文件 index.js 中,導(dǎo)出 qiankun 主應(yīng)用所需要的三個(gè)生命周期鉤子函數(shù),代碼實(shí)現(xiàn)如下:

從上圖來分析:
第 5 行:webpack默認(rèn)的publicPath為""空字符串,會基于當(dāng)前路徑來加載資源。我們在主應(yīng)用中加載微應(yīng)用時(shí)需要重新設(shè)置publicPath,這樣才能正確加載微應(yīng)用的相關(guān)資源。(public-path.js具體實(shí)現(xiàn)在后面)第 12 行:微應(yīng)用的掛載函數(shù),在主應(yīng)用中運(yùn)行時(shí)將在mount生命周期鉤子函數(shù)中調(diào)用,可以保證在沙箱內(nèi)運(yùn)行。第 17 行:微應(yīng)用獨(dú)立運(yùn)行時(shí),直接執(zhí)行render函數(shù)掛載微應(yīng)用。第 25 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -bootstrap。第 32 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -mount。第 40 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -unmount。
完整代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-react/src/public-path.js
"color: #c678dd;line-height: 26px;">if?("color: #e6c07b;line-height: 26px;">window.__POWERED_BY_QIANKUN__)?{
??"color: #5c6370;font-style: italic;line-height: 26px;">//?動態(tài)設(shè)置?webpack?publicPath,防止資源加載出錯
??"color: #5c6370;font-style: italic;line-height: 26px;">//?eslint-disable-next-line?no-undef
??__webpack_public_path__?=?"color: #e6c07b;line-height: 26px;">window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-react/src/index.js
"color: #c678dd;line-height: 26px;">import?React?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"react";
"color: #c678dd;line-height: 26px;">import?ReactDOM?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"react-dom";
"color: #c678dd;line-height: 26px;">import?"color: #98c379;line-height: 26px;">"antd/dist/antd.css";
"color: #c678dd;line-height: 26px;">import?"color: #98c379;line-height: 26px;">"./public-path";
"color: #c678dd;line-height: 26px;">import?App?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./App.jsx";
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?渲染函數(shù)
?*?兩種情況:主應(yīng)用生命周期鉤子中運(yùn)行?/?微應(yīng)用單獨(dú)啟動時(shí)運(yùn)行
?*/
"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">render("line-height: 26px;">)?{
??ReactDOM.render("line-height: 26px;">"line-height: 26px;"><"color: #e06c75;line-height: 26px;">App?/>,?"color: #e6c07b;line-height: 26px;">document.getElementById("color: #98c379;line-height: 26px;">"root"));
}
"color: #5c6370;font-style: italic;line-height: 26px;">//?獨(dú)立運(yùn)行時(shí),直接掛載應(yīng)用
"color: #c678dd;line-height: 26px;">if?(!"color: #e6c07b;line-height: 26px;">window.__POWERED_BY_QIANKUN__)?{
??render();
}
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?* bootstrap 只會在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會直接調(diào)用 mount 鉤子,不會再重復(fù)觸發(fā) bootstrap。
?*?通常我們可以在這里做一些全局變量的初始化,比如不會在 unmount 階段被銷毀的應(yīng)用級別的緩存等。
?*/
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">async?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">bootstrap("line-height: 26px;">)?{
??"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"ReactMicroApp?bootstraped");
}
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?應(yīng)用每次進(jìn)入都會調(diào)用?mount?方法,通常我們在這里觸發(fā)應(yīng)用的渲染方法
?*/
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">async?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">mount("line-height: 26px;">props)?{
??"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"ReactMicroApp?mount",?props);
??render(props);
}
"color: #5c6370;font-style: italic;line-height: 26px;">/**
?*?應(yīng)用每次?切出/卸載?會調(diào)用的方法,通常在這里我們會卸載微應(yīng)用的應(yīng)用實(shí)例
?*/
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">async?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">unmount("line-height: 26px;">)?{
??"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"ReactMicroApp?unmount");
??ReactDOM.unmountComponentAtNode("color: #e6c07b;line-height: 26px;">document.getElementById("color: #98c379;line-height: 26px;">"root"));
}
在配置好了入口文件 index.js 后,我們還需要配置路由命名空間,以確保主應(yīng)用可以正確加載微應(yīng)用,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-react/src/App.jsx
"color: #c678dd;line-height: 26px;">const?BASE_NAME?=?"color: #e6c07b;line-height: 26px;">window.__POWERED_BY_QIANKUN__???"color: #98c379;line-height: 26px;">"/react"?:?"color: #98c379;line-height: 26px;">"";
"color: #c678dd;line-height: 26px;">const?App?=?"line-height: 26px;">"line-height: 26px;">()?=>?{
??"color: #5c6370;font-style: italic;line-height: 26px;">//...
??"color: #c678dd;line-height: 26px;">return?(
????"color: #5c6370;font-style: italic;line-height: 26px;">//?設(shè)置路由命名空間
????"line-height: 26px;">"line-height: 26px;"><"color: #e06c75;line-height: 26px;">Router?"color: #d19a66;line-height: 26px;">basename="color: #98c379;line-height: 26px;">{BASE_NAME}>{/*?...?*/}"line-height: 26px;">"color: #e06c75;line-height: 26px;">Router>
??);
};
接下來,我們還需要配置 webpack,使 index.js 導(dǎo)出的生命周期鉤子函數(shù)可以被 qiankun 識別獲取。
我們需要借助 react-app-rewired 來幫助我們修改 webpack 的配置,我們直接安裝該插件:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">npm?install?react-app-rewired?-D
在 react-app-rewired 安裝完成后,我們還需要修改 package.json 的 scripts 選項(xiàng),修改為由 react-app-rewired 啟動應(yīng)用,就像下面這樣
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-react/package.json
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #98c379;line-height: 26px;">"scripts":?{
??"color: #d19a66;line-height: 26px;">"start":?"color: #98c379;line-height: 26px;">"react-app-rewired?start",
??"color: #d19a66;line-height: 26px;">"build":?"color: #98c379;line-height: 26px;">"react-app-rewired?build",
??"color: #d19a66;line-height: 26px;">"test":?"color: #98c379;line-height: 26px;">"react-app-rewired?test",
??"color: #d19a66;line-height: 26px;">"eject":?"color: #98c379;line-height: 26px;">"react-app-rewired?eject"
}
在 react-app-rewired 配置完成后,我們新建 config-overrides.js 文件來配置 webpack,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #c678dd;line-height: 26px;">const?path?=?"color: #e6c07b;line-height: 26px;">require("color: #98c379;line-height: 26px;">"path");
"color: #e6c07b;line-height: 26px;">module.exports?=?{
??"color: #d19a66;line-height: 26px;">webpack:?"line-height: 26px;">("line-height: 26px;">config)?=>?{
????"color: #5c6370;font-style: italic;line-height: 26px;">//?微應(yīng)用的包名,這里與主應(yīng)用中注冊的微應(yīng)用名稱一致
????config.output.library?=?"color: #98c379;line-height: 26px;">`ReactMicroApp`;
????"color: #5c6370;font-style: italic;line-height: 26px;">//?將你的?library?暴露為所有的模塊定義下都可運(yùn)行的方式
????config.output.libraryTarget?=?"color: #98c379;line-height: 26px;">"umd";
????"color: #5c6370;font-style: italic;line-height: 26px;">//?按需加載相關(guān),設(shè)置為?webpackJsonp_VueMicroApp?即可
????config.output.jsonpFunction?=?"color: #98c379;line-height: 26px;">`webpackJsonp_ReactMicroApp`;
????config.resolve.alias?=?{
??????...config.resolve.alias,
??????"color: #98c379;line-height: 26px;">"@":?path.resolve(__dirname,?"color: #98c379;line-height: 26px;">"src"),
????};
????"color: #c678dd;line-height: 26px;">return?config;
??},
??"color: #d19a66;line-height: 26px;">devServer:?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?("line-height: 26px;">configFunction)?{
????"color: #c678dd;line-height: 26px;">return?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?("line-height: 26px;">proxy,?allowedHost)?{
??????"color: #c678dd;line-height: 26px;">const?config?=?configFunction(proxy,?allowedHost);
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?關(guān)閉主機(jī)檢查,使微應(yīng)用可以被?fetch
??????config.disableHostCheck?=?"color: #56b6c2;line-height: 26px;">true;
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?配置跨域請求頭,解決開發(fā)環(huán)境的跨域問題
??????config.headers?=?{
????????"color: #98c379;line-height: 26px;">"Access-Control-Allow-Origin":?"color: #98c379;line-height: 26px;">"*",
??????};
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?配置?history?模式
??????config.historyApiFallback?=?"color: #56b6c2;line-height: 26px;">true;
??????"color: #c678dd;line-height: 26px;">return?config;
????};
??},
};
我們需要重點(diǎn)關(guān)注一下 output 選項(xiàng),當(dāng)我們把 libraryTarget 設(shè)置為 umd 后,我們的 library 就暴露為所有的模塊定義下都可運(yùn)行的方式了,主應(yīng)用就可以獲取到微應(yīng)用的生命周期鉤子函數(shù)了。
在 config-overrides.js 修改完成后,我們重新啟動 React 微應(yīng)用,然后打開主應(yīng)用基座 http://localhost:9999。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用,此時(shí)我們的 React 微應(yīng)用被正確加載啦?。ㄒ娤聢D)

此時(shí)我們打開控制臺,可以看到我們所執(zhí)行的生命周期鉤子函數(shù)(見下圖)

到這里,React 微應(yīng)用就接入成功了!
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">接入 Angular 微應(yīng)用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
Angular 與 qiankun 目前的兼容性并不太好,接入 Angular 微應(yīng)用需要一定的耐心與技巧。
對于選擇
Angular技術(shù)棧的前端開發(fā)來說,對這類情況應(yīng)該駕輕就熟(沒有辦法)。
我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例,我們在主應(yīng)用的同級目錄(micro-app-main 同級目錄),使用 @angular/cli 先創(chuàng)建一個(gè) Angular 的項(xiàng)目,在命令行運(yùn)行如下命令:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">ng?new?micro-app-angular
本文的 @angular/cli 選項(xiàng)如下圖所示,你也可以根據(jù)自己的喜好選擇配置。

然后,我們創(chuàng)建幾個(gè)路由頁面再加上一些樣式,最后效果如下:


"display: none;">注冊微應(yīng)用"display: none;">
在創(chuàng)建好了 Angular 微應(yīng)用后,我們可以開始我們的接入工作了。首先我們需要在主應(yīng)用中注冊該微應(yīng)用的信息,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/micro/apps.ts
"color: #c678dd;line-height: 26px;">const?apps?=?[
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?name:?微應(yīng)用名稱?-?具有唯一性
???*?entry:?微應(yīng)用入口?-?通過該地址加載微應(yīng)用
???*?container:?微應(yīng)用掛載節(jié)點(diǎn)?-?微應(yīng)用加載完成后將掛載在該節(jié)點(diǎn)上
???*?activeRule:?微應(yīng)用觸發(fā)的路由規(guī)則?-?觸發(fā)路由規(guī)則后將加載該微應(yīng)用
???*/
??{
????name:?"color: #98c379;line-height: 26px;">"AngularMicroApp",
????entry:?"color: #98c379;line-height: 26px;">"http://localhost:10300",
????container:?"color: #98c379;line-height: 26px;">"#frame",
????activeRule:?"color: #98c379;line-height: 26px;">"/angular",
??},
];
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?apps;
通過上面的代碼,我們就在主應(yīng)用中注冊了我們的 Angular 微應(yīng)用,進(jìn)入 /angular 路由時(shí)將加載我們的 Angular 微應(yīng)用。
我們在菜單配置處也加入 Angular 微應(yīng)用的快捷入口,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/App.vue
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?"color: #c678dd;line-height: 26px;">class?App?"color: #c678dd;line-height: 26px;">extends?Vue?{
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?菜單列表
???*?key:?唯一?Key?值
???*?title:?菜單標(biāo)題
???*?path:?菜單對應(yīng)的路徑
???*/
??menus?=?[
????{
??????key:?"color: #98c379;line-height: 26px;">"Home",
??????title:?"color: #98c379;line-height: 26px;">"主頁",
??????path:?"color: #98c379;line-height: 26px;">"/",
????},
????{
??????key:?"color: #98c379;line-height: 26px;">"AngularMicroApp",
??????title:?"color: #98c379;line-height: 26px;">"Angular?主頁",
??????path:?"color: #98c379;line-height: 26px;">"/angular",
????},
????{
??????key:?"color: #98c379;line-height: 26px;">"AngularMicroAppList",
??????title:?"color: #98c379;line-height: 26px;">"Angular?列表頁",
??????path:?"color: #98c379;line-height: 26px;">"/angular/list",
????},
??];
}
菜單配置完成后,我們的主應(yīng)用基座效果圖如下

最后我們在主應(yīng)用的入口文件,引入 zone.js,代碼實(shí)現(xiàn)如下:
Angular運(yùn)行依賴于zone.js。
qiankun基于single-spa實(shí)現(xiàn),single-spa明確指出一個(gè)項(xiàng)目的zone.js只能存在一份實(shí)例,所以我們在主應(yīng)用注入zone.js。
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/main.js
"color: #5c6370;font-style: italic;line-height: 26px;">//?為?Angular?微應(yīng)用所做的?zone?包注入
"color: #c678dd;line-height: 26px;">import?"color: #98c379;line-height: 26px;">"zone.js/dist/zone";
"display: none;">配置微應(yīng)用"display: none;">
在主應(yīng)用的工作完成后,我們還需要對微應(yīng)用進(jìn)行一系列的配置。首先,我們使用 single-spa-angular 生成一套配置,在命令行運(yùn)行以下命令:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">#?安裝?single-spa
yarn?add?single-spa?-S
"color: #5c6370;font-style: italic;line-height: 26px;">#?添加?single-spa-angular
ng?add?single-spa-angular
運(yùn)行命令時(shí),根據(jù)自己的需求選擇配置即可,本文配置如下:

在生成 single-spa 配置后,我們需要進(jìn)行一些 qiankun 的接入配置。我們在 Angular 微應(yīng)用的入口文件 main.single-spa.ts 中,導(dǎo)出 qiankun 主應(yīng)用所需要的三個(gè)生命周期鉤子函數(shù),代碼實(shí)現(xiàn)如下:

從上圖來分析:
第 21 行:微應(yīng)用獨(dú)立運(yùn)行時(shí),直接執(zhí)行掛載函數(shù)掛載微應(yīng)用。第 46 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -bootstrap。第 50 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -mount。第 54 行:微應(yīng)用導(dǎo)出的生命周期鉤子函數(shù) -unmount。
完整代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-angular/src/main.single-spa.ts
"color: #c678dd;line-height: 26px;">import?{?enableProdMode,?NgZone?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"@angular/core";
"color: #c678dd;line-height: 26px;">import?{?platformBrowserDynamic?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"@angular/platform-browser-dynamic";
"color: #c678dd;line-height: 26px;">import?{?Router?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"@angular/router";
"color: #c678dd;line-height: 26px;">import?{??AnimationEngine?"color: #c678dd;line-height: 26px;">as?AnimationEngine?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"@angular/animations/browser";
"color: #c678dd;line-height: 26px;">import?{
??singleSpaAngular,
??getSingleSpaExtraProviders,
}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"single-spa-angular";
"color: #c678dd;line-height: 26px;">import?{?AppModule?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./app/app.module";
"color: #c678dd;line-height: 26px;">import?{?environment?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./environments/environment";
"color: #c678dd;line-height: 26px;">import?{?singleSpaPropsSubject?}?"color: #c678dd;line-height: 26px;">from?"color: #98c379;line-height: 26px;">"./single-spa/single-spa-props";
"color: #c678dd;line-height: 26px;">if?(environment.production)?{
??enableProdMode();
}
"color: #5c6370;font-style: italic;line-height: 26px;">//?微應(yīng)用單獨(dú)啟動時(shí)運(yùn)行
"color: #c678dd;line-height: 26px;">if?(!("color: #e6c07b;line-height: 26px;">window?"color: #c678dd;line-height: 26px;">as?"color: #e6c07b;line-height: 26px;">any).__POWERED_BY_QIANKUN__)?{
??platformBrowserDynamic()
????.bootstrapModule(AppModule)
????.catch("line-height: 26px;">("line-height: 26px;">err)?=>?"color: #e6c07b;line-height: 26px;">console.error(err));
}
"color: #c678dd;line-height: 26px;">const?{?bootstrap,?mount,?unmount?}?=?singleSpaAngular({
??bootstrapFunction:?"line-height: 26px;">("line-height: 26px;">singleSpaProps)?=>?{
????singleSpaPropsSubject.next(singleSpaProps);
????"color: #c678dd;line-height: 26px;">return?platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(
??????AppModule
????);
??},
??template:?"color: #98c379;line-height: 26px;">" ",
??Router,
??NgZone,
??AnimationEngine,
});
"color: #5c6370;font-style: italic;line-height: 26px;">/**?主應(yīng)用生命周期鉤子中運(yùn)行?*/
"color: #c678dd;line-height: 26px;">export?{
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???* bootstrap 只會在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會直接調(diào)用 mount 鉤子,不會再重復(fù)觸發(fā) bootstrap。
???*?通常我們可以在這里做一些全局變量的初始化,比如不會在 unmount 階段被銷毀的應(yīng)用級別的緩存等。
???*/
??bootstrap,
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?應(yīng)用每次進(jìn)入都會調(diào)用?mount?方法,通常我們在這里觸發(fā)應(yīng)用的渲染方法
???*/
??mount,
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?應(yīng)用每次?切出/卸載?會調(diào)用的方法,通常在這里我們會卸載微應(yīng)用的應(yīng)用實(shí)例
???*/
??unmount,
};
在配置好了入口文件 main.single-spa.ts 后,我們還需要配置 webpack,使 main.single-spa.ts 導(dǎo)出的生命周期鉤子函數(shù)可以被 qiankun 識別獲取。
我們直接配置 extra-webpack.config.js 即可,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-angular/extra-webpack.config.js
"color: #c678dd;line-height: 26px;">const?singleSpaAngularWebpack?=?"color: #e6c07b;line-height: 26px;">require("color: #98c379;line-height: 26px;">"single-spa-angular/lib/webpack")
??.default;
"color: #c678dd;line-height: 26px;">const?webpackMerge?=?"color: #e6c07b;line-height: 26px;">require("color: #98c379;line-height: 26px;">"webpack-merge");
"color: #e6c07b;line-height: 26px;">module.exports?=?"line-height: 26px;">("line-height: 26px;">angularWebpackConfig,?options)?=>?{
??"color: #c678dd;line-height: 26px;">const?singleSpaWebpackConfig?=?singleSpaAngularWebpack(
????angularWebpackConfig,
????options
??);
??"color: #c678dd;line-height: 26px;">const?singleSpaConfig?=?{
????"color: #d19a66;line-height: 26px;">output:?{
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?微應(yīng)用的包名,這里與主應(yīng)用中注冊的微應(yīng)用名稱一致
??????"color: #d19a66;line-height: 26px;">library:?"color: #98c379;line-height: 26px;">"AngularMicroApp",
??????"color: #5c6370;font-style: italic;line-height: 26px;">//?將你的?library?暴露為所有的模塊定義下都可運(yùn)行的方式
??????"color: #d19a66;line-height: 26px;">libraryTarget:?"color: #98c379;line-height: 26px;">"umd",
????},
??};
??"color: #c678dd;line-height: 26px;">const?mergedConfig?=?webpackMerge.smart(
????singleSpaWebpackConfig,
????singleSpaConfig
??);
??"color: #c678dd;line-height: 26px;">return?mergedConfig;
};
我們需要重點(diǎn)關(guān)注一下 output 選項(xiàng),當(dāng)我們把 libraryTarget 設(shè)置為 umd 后,我們的 library 就暴露為所有的模塊定義下都可運(yùn)行的方式了,主應(yīng)用就可以獲取到微應(yīng)用的生命周期鉤子函數(shù)了。
在 extra-webpack.config.js 修改完成后,我們還需要修改一下 package.json 中的啟動命令,修改如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-angular/package.json
{
??"color: #5c6370;font-style: italic;line-height: 26px;">//...
??"color: #d19a66;line-height: 26px;">"script":?{
????"color: #5c6370;font-style: italic;line-height: 26px;">//...
????"color: #5c6370;font-style: italic;line-height: 26px;">//?--disable-host-check:?關(guān)閉主機(jī)檢查,使微應(yīng)用可以被?fetch
????"color: #5c6370;font-style: italic;line-height: 26px;">//?--port:?監(jiān)聽端口
????"color: #5c6370;font-style: italic;line-height: 26px;">//?--base-href:?站點(diǎn)的起始路徑,與主應(yīng)用中配置的一致
????"color: #d19a66;line-height: 26px;">"start":?"color: #98c379;line-height: 26px;">"ng?serve?--disable-host-check?--port?10300?--base-href?/angular"
??}
}
修改完成后,我們重新啟動 Angular 微應(yīng)用,然后打開主應(yīng)用基座 http://localhost:9999。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用,此時(shí)我們的 Angular 微應(yīng)用被正確加載啦!(見下圖)

到這里,Angular 微應(yīng)用就接入成功了!
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">接入 Jquery、xxx... 微應(yīng)用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
這里的
Jquery、xxx...微應(yīng)用指的是沒有使用腳手架,直接采用html + css + js三劍客開發(fā)的應(yīng)用。本案例使用了一些高級
ES語法,請使用谷歌瀏覽器運(yùn)行查看效果。
我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例,我們在主應(yīng)用的同級目錄(micro-app-main 同級目錄),手動創(chuàng)建目錄 micro-app-static。
我們使用 express 作為服務(wù)器加載靜態(tài) html,我們先編輯 package.json,設(shè)置啟動命令和相關(guān)依賴。
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-static/package.json
{
??"color: #d19a66;line-height: 26px;">"name":?"color: #98c379;line-height: 26px;">"micro-app-jquery",
??"color: #d19a66;line-height: 26px;">"version":?"color: #98c379;line-height: 26px;">"1.0.0",
??"color: #d19a66;line-height: 26px;">"description":?"color: #98c379;line-height: 26px;">"",
??"color: #d19a66;line-height: 26px;">"main":?"color: #98c379;line-height: 26px;">"index.js",
??"color: #d19a66;line-height: 26px;">"scripts":?{
????"color: #d19a66;line-height: 26px;">"start":?"color: #98c379;line-height: 26px;">"nodemon?index.js"
??},
??"color: #d19a66;line-height: 26px;">"author":?"color: #98c379;line-height: 26px;">"",
??"color: #d19a66;line-height: 26px;">"license":?"color: #98c379;line-height: 26px;">"ISC",
??"color: #d19a66;line-height: 26px;">"dependencies":?{
????"color: #d19a66;line-height: 26px;">"express":?"color: #98c379;line-height: 26px;">"^4.17.1",
????"color: #d19a66;line-height: 26px;">"cors":?"color: #98c379;line-height: 26px;">"^2.8.5"
??},
??"color: #d19a66;line-height: 26px;">"devDependencies":?{
????"color: #d19a66;line-height: 26px;">"nodemon":?"color: #98c379;line-height: 26px;">"^2.0.2"
??}
}
然后添加入口文件 index.js,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-static/index.js
"color: #c678dd;line-height: 26px;">const?express?=?"color: #e6c07b;line-height: 26px;">require("color: #98c379;line-height: 26px;">"express");
"color: #c678dd;line-height: 26px;">const?cors?=?"color: #e6c07b;line-height: 26px;">require("color: #98c379;line-height: 26px;">"cors");
"color: #c678dd;line-height: 26px;">const?app?=?express();
"color: #5c6370;font-style: italic;line-height: 26px;">//?解決跨域問題
app.use(cors());
app.use("color: #98c379;line-height: 26px;">'/',?express.static("color: #98c379;line-height: 26px;">'static'));
"color: #5c6370;font-style: italic;line-height: 26px;">//?監(jiān)聽端口
app.listen("color: #d19a66;line-height: 26px;">10400,?()?=>?{
??"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"server?is?listening?in?http://localhost:10400")
});
使用 npm install 安裝相關(guān)依賴后,我們使用 npm start 啟動應(yīng)用。
我們新建 static 文件夾,在文件夾內(nèi)新增一個(gè)靜態(tài)頁面 index.html(代碼在后面會貼出),加上一些樣式后,打開瀏覽器,最后效果如下:

"display: none;">注冊微應(yīng)用"display: none;">
在創(chuàng)建好了 Static 微應(yīng)用后,我們可以開始我們的接入工作了。首先我們需要在主應(yīng)用中注冊該微應(yīng)用的信息,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/micro/apps.ts
"color: #c678dd;line-height: 26px;">const?apps?=?[
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?name:?微應(yīng)用名稱?-?具有唯一性
???*?entry:?微應(yīng)用入口?-?通過該地址加載微應(yīng)用
???*?container:?微應(yīng)用掛載節(jié)點(diǎn)?-?微應(yīng)用加載完成后將掛載在該節(jié)點(diǎn)上
???*?activeRule:?微應(yīng)用觸發(fā)的路由規(guī)則?-?觸發(fā)路由規(guī)則后將加載該微應(yīng)用
???*/
??{
????name:?"color: #98c379;line-height: 26px;">"StaticMicroApp",
????entry:?"color: #98c379;line-height: 26px;">"http://localhost:10400",
????container:?"color: #98c379;line-height: 26px;">"#frame",
????activeRule:?"color: #98c379;line-height: 26px;">"/static"
??},
];
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?apps;
通過上面的代碼,我們就在主應(yīng)用中注冊了我們的 Static 微應(yīng)用,進(jìn)入 /static 路由時(shí)將加載我們的 Static 微應(yīng)用。
我們在菜單配置處也加入 Static 微應(yīng)用的快捷入口,代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">//?micro-app-main/src/App.vue
"color: #5c6370;font-style: italic;line-height: 26px;">//...
"color: #c678dd;line-height: 26px;">export?"color: #c678dd;line-height: 26px;">default?"color: #c678dd;line-height: 26px;">class?App?"color: #c678dd;line-height: 26px;">extends?Vue?{
??"color: #5c6370;font-style: italic;line-height: 26px;">/**
???*?菜單列表
???*?key:?唯一?Key?值
???*?title:?菜單標(biāo)題
???*?path:?菜單對應(yīng)的路徑
???*/
??menus?=?[
????{
??????key:?"color: #98c379;line-height: 26px;">"Home",
??????title:?"color: #98c379;line-height: 26px;">"主頁",
??????path:?"color: #98c379;line-height: 26px;">"/"
????},
????{
??????key:?"color: #98c379;line-height: 26px;">"StaticMicroApp",
??????title:?"color: #98c379;line-height: 26px;">"Static?微應(yīng)用",
??????path:?"color: #98c379;line-height: 26px;">"/static"
????}
??];
}
菜單配置完成后,我們的主應(yīng)用基座效果圖如下

"display: none;">配置微應(yīng)用"display: none;">
在主應(yīng)用注冊好了微應(yīng)用后,我們還需要直接寫微應(yīng)用 index.html 的代碼即可,代碼實(shí)現(xiàn)如下:

從上圖來分析:
第 70 行:微應(yīng)用的掛載函數(shù),在主應(yīng)用中運(yùn)行時(shí)將在mount生命周期鉤子函數(shù)中調(diào)用,可以保證在沙箱內(nèi)運(yùn)行。第 77 行:微應(yīng)用獨(dú)立運(yùn)行時(shí),直接執(zhí)行render函數(shù)掛載微應(yīng)用。第 88 行:微應(yīng)用注冊的生命周期鉤子函數(shù) -bootstrap。第 95 行:微應(yīng)用注冊的生命周期鉤子函數(shù) -mount。第 102 行:微應(yīng)用注冊的生命周期鉤子函數(shù) -unmount。
完整代碼實(shí)現(xiàn)如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">
"color: #61aeee;line-height: 26px;">html>
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">html?"color: #d19a66;line-height: 26px;">lang="color: #98c379;line-height: 26px;">"en">
??"line-height: 26px;"><"color: #e06c75;line-height: 26px;">head>
????"line-height: 26px;"><"color: #e06c75;line-height: 26px;">meta?"color: #d19a66;line-height: 26px;">charset="color: #98c379;line-height: 26px;">"UTF-8"?/>
????"line-height: 26px;"><"color: #e06c75;line-height: 26px;">meta?"color: #d19a66;line-height: 26px;">name="color: #98c379;line-height: 26px;">"viewport"?"color: #d19a66;line-height: 26px;">content="color: #98c379;line-height: 26px;">"width=device-width,?initial-scale=1.0"?/>
????"line-height: 26px;"><"color: #e06c75;line-height: 26px;">meta?"color: #d19a66;line-height: 26px;">http-equiv="color: #98c379;line-height: 26px;">"X-UA-Compatible"?"color: #d19a66;line-height: 26px;">content="color: #98c379;line-height: 26px;">"ie=edge"?/>
????"color: #5c6370;font-style: italic;line-height: 26px;">
????"line-height: 26px;"><"color: #e06c75;line-height: 26px;">link
??????"color: #d19a66;line-height: 26px;">href="color: #98c379;line-height: 26px;">"https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"
??????"color: #d19a66;line-height: 26px;">rel="color: #98c379;line-height: 26px;">"stylesheet"
????/>
????"line-height: 26px;"><"color: #e06c75;line-height: 26px;">title>Jquery?App"line-height: 26px;">"color: #e06c75;line-height: 26px;">title>
??"line-height: 26px;">"color: #e06c75;line-height: 26px;">head>
??"line-height: 26px;"><"color: #e06c75;line-height: 26px;">body>
????"line-height: 26px;"><"color: #e06c75;line-height: 26px;">section
??????"color: #d19a66;line-height: 26px;">id="color: #98c379;line-height: 26px;">"jquery-app-container"
??????"color: #d19a66;line-height: 26px;">style="color: #98c379;line-height: 26px;">"padding:?20px;?color:?blue;"
????>"line-height: 26px;">"color: #e06c75;line-height: 26px;">section>
??"line-height: 26px;">"color: #e06c75;line-height: 26px;">body>
??"color: #5c6370;font-style: italic;line-height: 26px;">
??"line-height: 26px;"><"color: #e06c75;line-height: 26px;">script?"color: #d19a66;line-height: 26px;">src="color: #98c379;line-height: 26px;">"https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js">"line-height: 26px;">"color: #e06c75;line-height: 26px;">script>
??"line-height: 26px;"><"color: #e06c75;line-height: 26px;">script>"line-height: 26px;">
????"color: #5c6370;font-style: italic;line-height: 26px;">/**
?????*?請求接口數(shù)據(jù),構(gòu)建?HTML
?????*/
????"color: #c678dd;line-height: 26px;">async?"line-height: 26px;">"color: #c678dd;line-height: 26px;">function?"color: #61aeee;line-height: 26px;">buildHTML("line-height: 26px;">)?{
??????"color: #c678dd;line-height: 26px;">const?result?=?"color: #c678dd;line-height: 26px;">await?fetch("color: #98c379;line-height: 26px;">"http://dev-api.jt-gmall.com/mall",?{
????????"color: #d19a66;line-height: 26px;">method:?"color: #98c379;line-height: 26px;">"POST",
????????"color: #d19a66;line-height: 26px;">headers:?{
??????????"color: #98c379;line-height: 26px;">"Content-Type":?"color: #98c379;line-height: 26px;">"application/json",
????????},
????????"color: #5c6370;font-style: italic;line-height: 26px;">//?graphql?的查詢風(fēng)格
????????"color: #d19a66;line-height: 26px;">body:?"color: #e6c07b;line-height: 26px;">JSON.stringify({
??????????"color: #d19a66;line-height: 26px;">query:?"color: #98c379;line-height: 26px;">`{?vegetableList?(page:?1,?pageSize:?20)?{?page,?pageSize,?total,?items?{?_id,?name,?poster,?price?}?}?}`,
????????}),
??????}).then("line-height: 26px;">("line-height: 26px;">res)?=>?res.json());
??????"color: #c678dd;line-height: 26px;">const?list?=?result.data.vegetableList.items;
??????"color: #c678dd;line-height: 26px;">const?html?=?"color: #98c379;line-height: 26px;">`
??
????
??????菜名
??????圖片
??????報(bào)價(jià)
????
??
??
????"color: #e06c75;line-height: 26px;">${list
??????.map(
????????(item)?=>?"color: #98c379;line-height: 26px;">`
????
??????
????????${item.poster}">
??????
??????"color: #e06c75;line-height: 26px;">${item.name}
??????¥?"color: #e06c75;line-height: 26px;">${item.price}
????
??????`
??????)
??????.join("color: #98c379;line-height: 26px;">"")}
??
`;
??????"color: #c678dd;line-height: 26px;">return?html;
????}
????"color: #5c6370;font-style: italic;line-height: 26px;">/**
?????*?渲染函數(shù)
?????*?兩種情況:主應(yīng)用生命周期鉤子中運(yùn)行?/?微應(yīng)用單獨(dú)啟動時(shí)運(yùn)行
?????*/
????"color: #c678dd;line-height: 26px;">const?render?=?"color: #c678dd;line-height: 26px;">async?($)?=>?{
??????"color: #c678dd;line-height: 26px;">const?html?=?"color: #c678dd;line-height: 26px;">await?buildHTML();
??????$("color: #98c379;line-height: 26px;">"#jquery-app-container").html(html);
??????"color: #c678dd;line-height: 26px;">return?"color: #e6c07b;line-height: 26px;">Promise.resolve();
????};
????"color: #5c6370;font-style: italic;line-height: 26px;">//?獨(dú)立運(yùn)行時(shí),直接掛載應(yīng)用
????"color: #c678dd;line-height: 26px;">if?(!"color: #e6c07b;line-height: 26px;">window.__POWERED_BY_QIANKUN__)?{
??????render($);
????}
????("line-height: 26px;">("line-height: 26px;">global)?=>?{
??????"color: #5c6370;font-style: italic;line-height: 26px;">/**
???????*?注冊微應(yīng)用生命周期鉤子函數(shù)
???????*?global[appName]?中的?appName?與主應(yīng)用中注冊的微應(yīng)用名稱一致
???????*/
??????global["color: #98c379;line-height: 26px;">"StaticMicroApp"]?=?{
????????"color: #5c6370;font-style: italic;line-height: 26px;">/**
?????????* bootstrap 只會在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會直接調(diào)用 mount 鉤子,不會再重復(fù)觸發(fā) bootstrap。
?????????*?通常我們可以在這里做一些全局變量的初始化,比如不會在 unmount 階段被銷毀的應(yīng)用級別的緩存等。
?????????*/
????????"color: #d19a66;line-height: 26px;">bootstrap:?"line-height: 26px;">"line-height: 26px;">()?=>?{
??????????"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"MicroJqueryApp?bootstraped");
??????????"color: #c678dd;line-height: 26px;">return?"color: #e6c07b;line-height: 26px;">Promise.resolve();
????????},
????????"color: #5c6370;font-style: italic;line-height: 26px;">/**
?????????*?應(yīng)用每次進(jìn)入都會調(diào)用?mount?方法,通常我們在這里觸發(fā)應(yīng)用的渲染方法
?????????*/
????????"color: #d19a66;line-height: 26px;">mount:?"line-height: 26px;">"line-height: 26px;">()?=>?{
??????????"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"MicroJqueryApp?mount");
??????????"color: #c678dd;line-height: 26px;">return?render($);
????????},
????????"color: #5c6370;font-style: italic;line-height: 26px;">/**
?????????*?應(yīng)用每次?切出/卸載?會調(diào)用的方法,通常在這里我們會卸載微應(yīng)用的應(yīng)用實(shí)例
?????????*/
????????"color: #d19a66;line-height: 26px;">unmount:?"line-height: 26px;">"line-height: 26px;">()?=>?{
??????????"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"MicroJqueryApp?unmount");
??????????"color: #c678dd;line-height: 26px;">return?"color: #e6c07b;line-height: 26px;">Promise.resolve();
????????},
??????};
????})("color: #e6c07b;line-height: 26px;">window);
??"line-height: 26px;">"color: #e06c75;line-height: 26px;">script>
"line-height: 26px;">"color: #e06c75;line-height: 26px;">html>
在構(gòu)建好了 Static 微應(yīng)用后,我們打開主應(yīng)用基座 http://localhost:9999。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用,此時(shí)可以看到,我們的 Static 微應(yīng)用被正確加載啦!(見下圖)

此時(shí)我們打開控制臺,可以看到我們所執(zhí)行的生命周期鉤子函數(shù)(見下圖)

到這里,Static 微應(yīng)用就接入成功了!
"display: none;">擴(kuò)展閱讀"display: none;">
如果在 Static 微應(yīng)用的 html 中注入 SPA 路由功能的話,將演變成單頁應(yīng)用,只需要在主應(yīng)用中注冊一次。
如果是多個(gè) html 的多頁應(yīng)用 - MPA,則需要在服務(wù)器(或反向代理服務(wù)器)中通過 referer 頭返回對應(yīng)的 html 文件,或者在主應(yīng)用中注冊多個(gè)微應(yīng)用(不推薦)。
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">小結(jié)"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
最后,我們所有微應(yīng)用都注冊在主應(yīng)用和主應(yīng)用的菜單中,效果圖如下:

從上圖可以看出,我們把不同技術(shù)棧 Vue、React、Angular、Jquery... 的微應(yīng)用都已經(jīng)接入到主應(yīng)用基座中啦!
"color: rgb(89, 89, 89);font-size: 18px;font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;text-align: left;">??愛心三連擊 1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的「點(diǎn)贊,在看」是我創(chuàng)作的動力。
2.關(guān)注公眾號
程序員成長指北,回復(fù)「1」加入Node進(jìn)階交流群!「在這里有好多 Node 開發(fā)者,會討論 Node 知識,互相學(xué)習(xí)」!3.也可添加微信【ikoala520】,一起成長。
“在看轉(zhuǎn)發(fā)”是最大的支持
