<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于 qiankun 的微前端最佳實(shí)踐(萬字長文) - 從 0 到 1 篇

          共 59622字,需瀏覽 120分鐘

           ·

          2020-12-17 12:09

          "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)用基座的教程盡量不涉及 VueAPI,涉及到 API 的地方都會給出解釋。

          注意:qiankun 屬于無侵入性的微前端框架,對主應(yīng)用基座和微應(yīng)用的技術(shù)棧都沒有要求。

          我們在本教程中,接入了多技術(shù)棧 微應(yīng)用主應(yīng)用 最終效果圖如下:

          micro-app

          "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)行三步操作:

          1. 創(chuàng)建微應(yīng)用容器 - 用于承載微應(yīng)用,渲染顯示微應(yīng)用;
          2. 注冊微應(yīng)用 - 設(shè)置微應(yīng)用激活條件,微應(yīng)用地址等等;
          3. 啟動 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ū)域(如下圖)

          micro-app

          我們來分析一下上面的代碼:

          • 第 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)效果如下圖所示:

          micro-app

          從上圖可以看出,我們主應(yīng)用的組件和微應(yīng)用是顯示在同一片內(nèi)容區(qū)域,根據(jù)路由規(guī)則決定渲染規(guī)則。

          "display: none;">注冊微應(yīng)用"display: none;">

          在構(gòu)建好了主框架后,我們需要使用 qiankunregisterMicroApps 方法注冊微應(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)用注冊信息),然后使用 qiankunregisterMicroApps 方法注冊微應(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)用,效果圖如下:

          micro-app

          因?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ù)(見下圖)。

          micro-app

          從上圖可以看出,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ù)自己的喜好選擇配置。

          micro-app

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

          micro-app
          micro-app

          "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)用基座效果圖如下

          micro-app

          "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)如下:

          micro-app

          從上圖來分析:

          • 第 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)

          micro-app

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

          micro-app

          到這里,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è)路由頁面再加上一些樣式,最后效果如下:

          micro-app
          micro-app

          "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)用基座效果圖如下

          micro-app

          "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)如下:

          micro-app

          從上圖來分析:

          • 第 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;">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.jsonscripts 選項(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)

          micro-app

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

          micro-app

          到這里,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;">

          Angularqiankun 目前的兼容性并不太好,接入 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ù)自己的喜好選擇配置。

          micro-app

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

          micro-app
          micro-app

          "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)用基座效果圖如下

          micro-app

          最后我們在主應(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ù)自己的需求選擇配置即可,本文配置如下:

          micro-app

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

          micro-app

          從上圖來分析:

          • 第 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)用被正確加載啦!(見下圖)

          micro-app

          到這里,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(代碼在后面會貼出),加上一些樣式后,打開瀏覽器,最后效果如下:

          micro-app

          "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)用基座效果圖如下

          micro-app

          "display: none;">配置微應(yīng)用"display: none;">

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

          micro-app

          從上圖來分析:

          • 第 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;">title>
          ??"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;">section>
          ??"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;">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;">script>
          "line-height: 26px;">html>

          在構(gòu)建好了 Static 微應(yīng)用后,我們打開主應(yīng)用基座 http://localhost:9999。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用,此時(shí)可以看到,我們的 Static 微應(yīng)用被正確加載啦!(見下圖)

          micro-app

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

          micro-app

          到這里,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)用的菜單中,效果圖如下:

          micro-app

          從上圖可以看出,我們把不同技術(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ā)”是最大的支持

          瀏覽 32
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.avzaixian | 青青草在线精品视频 | 国产一级黄片视频 | 俺去也天天幹 | 国产日逼视频观看 |