基于 qiankun 的微前端最佳實(shí)踐(圖文并茂) - 從 0 到 1 篇

micro-app

寫在開頭

微前端系列文章:

本系列其他文章計(jì)劃一到兩個(gè)月內(nèi)完成呆盖,點(diǎn)個(gè) 關(guān)注 不迷路着裹。

計(jì)劃如下:

  • 生命周期篇;
  • IE 兼容篇轧铁;
  • 生產(chǎn)環(huán)境部署篇端铛;
  • 性能優(yōu)化泣矛、緩存方案篇;

引言

大家好~

本文是基于 qiankun 的微前端最佳實(shí)踐系列文章之 從 0 到 1 篇沦补,本文將分享如何使用 qiankun 如何搭建主應(yīng)用基座乳蓄,然后接入不同技術(shù)棧的微應(yīng)用,完成微前端架構(gòu)的從 0 到 1夕膀。

本文采用 Vue 作為主應(yīng)用基座虚倒,接入不同技術(shù)棧的微應(yīng)用。如果你不懂 Vue 也沒關(guān)系产舞,我們?cè)诖罱ㄖ鲬?yīng)用基座的教程盡量不涉及 VueAPI魂奥,涉及到 API 的地方都會(huì)給出解釋。

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

構(gòu)建主應(yīng)用基座

我們以 實(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-cliVue 官方提供的腳手架工具炮赦,用于快速搭建一個(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. 注冊(cè)微應(yīng)用 - 設(shè)置微應(yīng)用激活條件植锉,微應(yīng)用地址等等;
  3. 啟動(dòng) qiankun峭拘;

創(chuàng)建微應(yīng)用容器

我們先在主應(yīng)用中創(chuàng)建微應(yīng)用的承載容器俊庇,這個(gè)容器規(guī)定了微應(yīng)用的顯示區(qū)域,微應(yīng)用將在該容器內(nèi)渲染并顯示棚唆。

我們先設(shè)置路由暇赤,路由文件規(guī)定了主應(yīng)用自身的路由匹配規(guī)則,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/routes/index.ts
import Home from "@/pages/home/index.vue";

const routes = [
  {
    /**
     * path: 路徑為 / 時(shí)觸發(fā)該路由規(guī)則
     * name: 路由的 name 為 Home
     * component: 觸發(fā)路由時(shí)加載 `Home` 組件
     */
    path: "/",
    name: "Home",
    component: Home,
  },
];

export default routes;

// micro-app-main/src/main.ts
//...
import Vue from "vue";
import VueRouter from "vue-router";

import routes from "./routes";

/**
 * 注冊(cè)路由實(shí)例
 * 即將開始監(jiān)聽 location 變化宵凌,觸發(fā)路由規(guī)則
 */
const router = new VueRouter({
  mode: "history",
  routes,
});

// 創(chuàng)建 Vue 實(shí)例
// 該實(shí)例將掛載/渲染在 id 為 main-app 的節(jié)點(diǎn)上
new Vue({
  router,
  render: (h) => h(App),
}).$mount("#main-app");

從上面代碼可以看出,我們?cè)O(shè)置了主應(yīng)用的路由規(guī)則止后,設(shè)置了 Home 主頁的路由匹配規(guī)則瞎惫。

我們現(xiàn)在來設(shè)置主應(yīng)用的布局,我們會(huì)有一個(gè)菜單和顯示區(qū)域译株,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/App.vue
//...
export default class App extends Vue {
  /**
   * 菜單列表
   * key: 唯一 Key 值
   * title: 菜單標(biāo)題
   * path: 菜單對(duì)應(yīng)的路徑
   */
  menus = [
    {
      key: "Home",
      title: "主頁",
      path: "/",
    },
  ];
}

上面的代碼是我們對(duì)菜單配置的實(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ī)則。

注冊(cè)微應(yīng)用

在構(gòu)建好了主框架后贰剥,我們需要使用 qiankunregisterMicroApps 方法注冊(cè)微應(yīng)用倾剿,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/micro/apps.ts
// 此時(shí)我們還沒有微應(yīng)用,所以 apps 為空
const apps = [];

export default apps;

// micro-app-main/src/micro/index.ts
// 一個(gè)進(jìn)度條插件
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { message } from "ant-design-vue";
import {
  registerMicroApps,
  addGlobalUncaughtErrorHandler,
  start,
} from "qiankun";

// 微應(yīng)用注冊(cè)信息
import apps from "./apps";

/**
 * 注冊(cè)微應(yīng)用
 * 第一個(gè)參數(shù) - 微應(yīng)用的注冊(cè)信息
 * 第二個(gè)參數(shù) - 全局生命周期鉤子
 */
registerMicroApps(apps, {
  // qiankun 生命周期鉤子 - 微應(yīng)用加載前
  beforeLoad: (app: any) => {
    // 加載微應(yīng)用前鸠澈,加載進(jìn)度條
    NProgress.start();
    console.log("before load", app.name);
    return Promise.resolve();
  },
  // qiankun 生命周期鉤子 - 微應(yīng)用掛載后
  afterMount: (app: any) => {
    // 加載微應(yīng)用前柱告,進(jìn)度條加載完成
    NProgress.done();
    console.log("after mount", app.name);
    return Promise.resolve();
  },
});

/**
 * 添加全局的未捕獲異常處理器
 */
addGlobalUncaughtErrorHandler((event: Event | string) => {
  console.error(event);
  const { message: msg } = event as any;
  // 加載失敗時(shí)提示
  if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) {
    message.error("微應(yīng)用加載失敗,請(qǐng)檢查應(yīng)用是否可運(yùn)行");
  }
});

// 導(dǎo)出 qiankun 的啟動(dòng)函數(shù)
export default start;

從上面可以看出笑陈,我們的微應(yīng)用注冊(cè)信息在 apps 數(shù)組中(此時(shí)為空际度,我們?cè)诤竺娼尤胛?yīng)用時(shí)會(huì)添加微應(yīng)用注冊(cè)信息),然后使用 qiankunregisterMicroApps 方法注冊(cè)微應(yīng)用涵妥,最后導(dǎo)出了 start 函數(shù)乖菱,注冊(cè)微應(yīng)用的工作就完成啦!

啟動(dòng)主應(yīng)用

我們?cè)谧?cè)好了微應(yīng)用蓬网,導(dǎo)出 start 函數(shù)后窒所,我們需要在合適的地方調(diào)用 start 啟動(dòng)主應(yīng)用。

我們一般是在入口文件啟動(dòng) qiankun 主應(yīng)用帆锋,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/main.ts
//...
import startQiankun from "./micro";

startQiankun();

最后吵取,啟動(dòng)我們的主應(yīng)用,效果圖如下:

micro-app

因?yàn)槲覀冞€沒有注冊(cè)任何微應(yīng)用锯厢,所以這里的效果圖和上面的效果圖是一樣的皮官。

到這一步,我們的主應(yīng)用基座就創(chuàng)建好啦实辑!

接入微應(yīng)用

我們現(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)部會(huì)校驗(yàn)微應(yīng)用的生命周期鉤子函數(shù)残黑,如果微應(yīng)用沒有導(dǎo)出這三個(gè)生命周期鉤子函數(shù)馍佑,則微應(yīng)用會(huì)加載失敗。

如果我們使用了腳手架搭建微應(yīng)用的話萍摊,我們可以通過 webpack 配置在入口文件處導(dǎo)出這三個(gè)生命周期鉤子函數(shù)挤茄。如果沒有使用腳手架的話,也可以直接在微應(yīng)用的 window 上掛載這三個(gè)生命周期鉤子函數(shù)冰木。

現(xiàn)在我們來接入我們的各個(gè)技術(shù)棧微應(yīng)用吧穷劈!

注意笼恰,下面的內(nèi)容對(duì)相關(guān)技術(shù)棧 API 不會(huì)再有過多介紹啦,如果你要接入不同技術(shù)棧的微應(yīng)用歇终,最好要對(duì)該技術(shù)棧有一些基礎(chǔ)了解社证。

接入 Vue 微應(yīng)用

我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例,我們?cè)谥鲬?yīng)用的同級(jí)目錄(micro-app-main 同級(jí)目錄)评凝,使用 vue-cli 先創(chuàng)建一個(gè) Vue 的項(xiàng)目追葡,在命令行運(yùn)行如下命令:

vue create micro-app-vue

本文的 vue-cli 選項(xiàng)如下圖所示,你也可以根據(jù)自己的喜好選擇配置奕短。

micro-app

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

micro-app
micro-app

注冊(cè)微應(yīng)用

在創(chuàng)建好了 Vue 微應(yīng)用后翎碑,我們可以開始我們的接入工作了谬返。首先我們需要在主應(yīng)用中注冊(cè)該微應(yīng)用的信息,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/micro/apps.ts
const apps = [
  /**
   * 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: "VueMicroApp",
    entry: "http://localhost:10200",
    container: "#frame",
    activeRule: "/vue",
  },
];

export default apps;

通過上面的代碼日杈,我們就在主應(yīng)用中注冊(cè)了我們的 Vue 微應(yīng)用遣铝,進(jìn)入 /vue 路由時(shí)將加載我們的 Vue 微應(yīng)用。

我們?cè)诓藛闻渲锰幰布尤?Vue 微應(yīng)用的快捷入口莉擒,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/App.vue
//...
export default class App extends Vue {
  /**
   * 菜單列表
   * key: 唯一 Key 值
   * title: 菜單標(biāo)題
   * path: 菜單對(duì)應(yīng)的路徑
   */
  menus = [
    {
      key: "Home",
      title: "主頁",
      path: "/",
    },
    {
      key: "VueMicroApp",
      title: "Vue 主頁",
      path: "/vue",
    },
    {
      key: "VueMicroAppList",
      title: "Vue 列表頁",
      path: "/vue/list",
    },
  ];
}

菜單配置完成后酿炸,我們的主應(yīng)用基座效果圖如下

micro-app

配置微應(yīng)用

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

micro-app

從上圖來分析:

  • 第 6 行webpack 默認(rèn)的 publicPath"" 空字符串廷支,會(huì)基于當(dāng)前路徑來加載資源。我們?cè)谥鲬?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)如下:

// micro-app-vue/src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
  // 動(dòng)態(tài)設(shè)置 webpack publicPath腋妙,防止資源加載出錯(cuò)
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// micro-app-vue/src/main.js
import Vue from "vue";
import VueRouter from "vue-router";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.css";

import "./public-path";
import App from "./App.vue";
import routes from "./routes";

Vue.use(VueRouter);
Vue.use(Antd);
Vue.config.productionTip = false;

let instance = null;
let router = null;

/**
 * 渲染函數(shù)
 * 兩種情況:主應(yīng)用生命周期鉤子中運(yùn)行 / 微應(yīng)用單獨(dú)啟動(dòng)時(shí)運(yùn)行
 */
function render() {
  // 在 render 中創(chuàng)建 VueRouter,可以保證在卸載微應(yīng)用時(shí)讯榕,移除 location 事件監(jiān)聽骤素,防止事件污染
  router = new VueRouter({
    // 運(yùn)行在主應(yīng)用中時(shí)匙睹,添加路由命名空間 /vue
    base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/",
    mode: "history",
    routes,
  });

  // 掛載應(yīng)用
  instance = new Vue({
    router,
    render: (h) => h(App),
  }).$mount("#app");
}

// 獨(dú)立運(yùn)行時(shí),直接掛載應(yīng)用
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

/**
 * bootstrap 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次济竹,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子痕檬,不會(huì)再重復(fù)觸發(fā) bootstrap。
 * 通常我們可以在這里做一些全局變量的初始化送浊,比如不會(huì)在 unmount 階段被銷毀的應(yīng)用級(jí)別的緩存等梦谜。
 */
export async function bootstrap() {
  console.log("VueMicroApp bootstraped");
}

/**
 * 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法
 */
export async function mount(props) {
  console.log("VueMicroApp mount", props);
  render(props);
}

/**
 * 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法袭景,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例
 */
export async function unmount() {
  console.log("VueMicroApp unmount");
  instance.$destroy();
  instance = null;
  router = null;
}

在配置好了入口文件 main.js 后唁桩,我們還需要配置 webpack,使 main.js 導(dǎo)出的生命周期鉤子函數(shù)可以被 qiankun 識(shí)別獲取耸棒。

我們直接配置 vue.config.js 即可荒澡,代碼實(shí)現(xiàn)如下:

// micro-app-vue/vue.config.js
const path = require("path");

module.exports = {
  devServer: {
    // 監(jiān)聽端口
    port: 10200,
    // 關(guān)閉主機(jī)檢查,使微應(yīng)用可以被 fetch
    disableHostCheck: true,
    // 配置跨域請(qǐng)求頭榆纽,解決開發(fā)環(huán)境的跨域問題
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
  configureWebpack: {
    resolve: {
      alias: {
        "@": path.resolve(__dirname, "src"),
      },
    },
    output: {
      // 微應(yīng)用的包名仰猖,這里與主應(yīng)用中注冊(cè)的微應(yīng)用名稱一致
      library: "VueMicroApp",
      // 將你的 library 暴露為所有的模塊定義下都可運(yùn)行的方式
      libraryTarget: "umd",
      // 按需加載相關(guān),設(shè)置為 webpackJsonp_VueMicroApp 即可
      jsonpFunction: `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 修改完成后躏升,我們重新啟動(dòng) Vue 微應(yīng)用,然后打開主應(yīng)用基座 http://localhost:9999狼忱。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用膨疏,此時(shí)我們的 Vue 微應(yīng)用被正確加載啦!(見下圖)

micro-app

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

micro-app

到這里佃却,Vue 微應(yīng)用就接入成功了!

接入 React 微應(yīng)用

我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例窘俺,我們?cè)谥鲬?yīng)用的同級(jí)目錄(micro-app-main 同級(jí)目錄)饲帅,使用 react-create-app 先創(chuàng)建一個(gè) React 的項(xiàng)目,在命令行運(yùn)行如下命令:

npx create-react-app micro-app-react

在項(xiàng)目創(chuàng)建完成后瘤泪,我們?cè)诟夸浵绿砑?.env 文件灶泵,設(shè)置項(xiàng)目監(jiān)聽的端口,代碼實(shí)現(xiàn)如下:

# micro-app-react/.env
PORT=10100
BROWSER=none

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

micro-app
micro-app

注冊(cè)微應(yīng)用

在創(chuàng)建好了 React 微應(yīng)用后,我們可以開始我們的接入工作了实檀。首先我們需要在主應(yīng)用中注冊(cè)該微應(yīng)用的信息惶洲,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/micro/apps.ts
const apps = [
  /**
   * 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: "ReactMicroApp",
    entry: "http://localhost:10100",
    container: "#frame",
    activeRule: "/react",
  },
];

export default apps;

通過上面的代碼按声,我們就在主應(yīng)用中注冊(cè)了我們的 React 微應(yīng)用,進(jìn)入 /react 路由時(shí)將加載我們的 React 微應(yīng)用湃鹊。

我們?cè)诓藛闻渲锰幰布尤?React 微應(yīng)用的快捷入口儒喊,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/App.vue
//...
export default class App extends Vue {
  /**
   * 菜單列表
   * key: 唯一 Key 值
   * title: 菜單標(biāo)題
   * path: 菜單對(duì)應(yīng)的路徑
   */
  menus = [
    {
      key: "Home",
      title: "主頁",
      path: "/",
    },
    {
      key: "ReactMicroApp",
      title: "React 主頁",
      path: "/react",
    },
    {
      key: "ReactMicroAppList",
      title: "React 列表頁",
      path: "/react/list",
    },
  ];
}

菜單配置完成后,我們的主應(yīng)用基座效果圖如下

micro-app

配置微應(yīng)用

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

micro-app

從上圖來分析:

  • 第 5 行webpack 默認(rèn)的 publicPath"" 空字符串,會(huì)基于當(dāng)前路徑來加載資源妻柒。我們?cè)谥鲬?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)如下:

// micro-app-react/src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
  // 動(dòng)態(tài)設(shè)置 webpack publicPath,防止資源加載出錯(cuò)
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// micro-app-react/src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";

import "./public-path";
import App from "./App.jsx";

/**
 * 渲染函數(shù)
 * 兩種情況:主應(yīng)用生命周期鉤子中運(yùn)行 / 微應(yīng)用單獨(dú)啟動(dòng)時(shí)運(yùn)行
 */
function render() {
  ReactDOM.render(<App />, document.getElementById("root"));
}

// 獨(dú)立運(yùn)行時(shí)拔第,直接掛載應(yīng)用
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

/**
 * bootstrap 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次咕村,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子,不會(huì)再重復(fù)觸發(fā) bootstrap蚊俺。
 * 通常我們可以在這里做一些全局變量的初始化懈涛,比如不會(huì)在 unmount 階段被銷毀的應(yīng)用級(jí)別的緩存等。
 */
export async function bootstrap() {
  console.log("ReactMicroApp bootstraped");
}

/**
 * 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法泳猬,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法
 */
export async function mount(props) {
  console.log("ReactMicroApp mount", props);
  render(props);
}

/**
 * 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法肩钠,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例
 */
export async function unmount() {
  console.log("ReactMicroApp unmount");
  ReactDOM.unmountComponentAtNode(document.getElementById("root"));
}

在配置好了入口文件 index.js 后,我們還需要配置路由命名空間暂殖,以確保主應(yīng)用可以正確加載微應(yīng)用,代碼實(shí)現(xiàn)如下:

// micro-app-react/src/App.jsx
const BASE_NAME = window.__POWERED_BY_QIANKUN__ ? "/react" : "";
const App = () => {
  //...

  return (
    // 設(shè)置路由命名空間
    <Router basename={BASE_NAME}>{/* ... */}</Router>
  );
};

接下來当纱,我們還需要配置 webpack呛每,使 index.js 導(dǎo)出的生命周期鉤子函數(shù)可以被 qiankun 識(shí)別獲取。

我們需要借助 react-app-rewired 來幫助我們修改 webpack 的配置坡氯,我們直接安裝該插件:

npm install react-app-rewired -D

react-app-rewired 安裝完成后晨横,我們還需要修改 package.jsonscripts 選項(xiàng)洋腮,修改為由 react-app-rewired 啟動(dòng)應(yīng)用,就像下面這樣

// micro-app-react/package.json

//...
"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test",
  "eject": "react-app-rewired eject"
}

react-app-rewired 配置完成后手形,我們新建 config-overrides.js 文件來配置 webpack啥供,代碼實(shí)現(xiàn)如下:

const path = require("path");

module.exports = {
  webpack: (config) => {
    // 微應(yīng)用的包名,這里與主應(yīng)用中注冊(cè)的微應(yīng)用名稱一致
    config.output.library = `ReactMicroApp`;
    // 將你的 library 暴露為所有的模塊定義下都可運(yùn)行的方式
    config.output.libraryTarget = "umd";
    // 按需加載相關(guān)库糠,設(shè)置為 webpackJsonp_VueMicroApp 即可
    config.output.jsonpFunction = `webpackJsonp_ReactMicroApp`;

    config.resolve.alias = {
      ...config.resolve.alias,
      "@": path.resolve(__dirname, "src"),
    };
    return config;
  },

  devServer: function (configFunction) {
    return function (proxy, allowedHost) {
      const config = configFunction(proxy, allowedHost);
      // 關(guān)閉主機(jī)檢查伙狐,使微應(yīng)用可以被 fetch
      config.disableHostCheck = true;
      // 配置跨域請(qǐng)求頭,解決開發(fā)環(huán)境的跨域問題
      config.headers = {
        "Access-Control-Allow-Origin": "*",
      };
      // 配置 history 模式
      config.historyApiFallback = true;

      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 修改完成后唉侄,我們重新啟動(dòng) React 微應(yīng)用,然后打開主應(yīng)用基座 http://localhost:9999野建。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用属划,此時(shí)我們的 React 微應(yīng)用被正確加載啦!(見下圖)

micro-app

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

micro-app

到這里同眯,React 微應(yīng)用就接入成功了!

接入 Angular 微應(yīng)用

Angularqiankun 目前的兼容性并不太好陶舞,接入 Angular 微應(yīng)用需要一定的耐心與技巧嗽测。

對(duì)于選擇 Angular 技術(shù)棧的前端開發(fā)來說,對(duì)這類情況應(yīng)該駕輕就熟(沒有辦法)肿孵。

我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例唠粥,我們?cè)谥鲬?yīng)用的同級(jí)目錄(micro-app-main 同級(jí)目錄),使用 @angular/cli 先創(chuàng)建一個(gè) Angular 的項(xiàng)目停做,在命令行運(yùn)行如下命令:

ng new micro-app-angular

本文的 @angular/cli 選項(xiàng)如下圖所示晤愧,你也可以根據(jù)自己的喜好選擇配置。

micro-app

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

micro-app
micro-app

注冊(cè)微應(yīng)用

在創(chuàng)建好了 Angular 微應(yīng)用后,我們可以開始我們的接入工作了烙丛。首先我們需要在主應(yīng)用中注冊(cè)該微應(yīng)用的信息舅巷,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/micro/apps.ts
const apps = [
  /**
   * 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: "AngularMicroApp",
    entry: "http://localhost:10300",
    container: "#frame",
    activeRule: "/angular",
  },
];

export default apps;

通過上面的代碼,我們就在主應(yīng)用中注冊(cè)了我們的 Angular 微應(yīng)用河咽,進(jìn)入 /angular 路由時(shí)將加載我們的 Angular 微應(yīng)用钠右。

我們?cè)诓藛闻渲锰幰布尤?Angular 微應(yīng)用的快捷入口,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/App.vue
//...
export default class App extends Vue {
  /**
   * 菜單列表
   * key: 唯一 Key 值
   * title: 菜單標(biāo)題
   * path: 菜單對(duì)應(yīng)的路徑
   */
  menus = [
    {
      key: "Home",
      title: "主頁",
      path: "/",
    },
    {
      key: "AngularMicroApp",
      title: "Angular 主頁",
      path: "/angular",
    },
    {
      key: "AngularMicroAppList",
      title: "Angular 列表頁",
      path: "/angular/list",
    },
  ];
}

菜單配置完成后忘蟹,我們的主應(yīng)用基座效果圖如下

micro-app

最后我們?cè)谥鲬?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í)例护糖,所以我們?cè)谥鲬?yīng)用注入 zone.js

// micro-app-main/src/main.js

// 為 Angular 微應(yīng)用所做的 zone 包注入
import "zone.js/dist/zone";

配置微應(yīng)用

在主應(yīng)用的工作完成后嚼松,我們還需要對(duì)微應(yīng)用進(jìn)行一系列的配置嫡良。首先,我們使用 single-spa-angular 生成一套配置惜颇,在命令行運(yùn)行以下命令:

# 安裝 single-spa
yarn add single-spa -S

# 添加 single-spa-angular
ng add single-spa-angular

運(yùn)行命令時(shí)皆刺,根據(jù)自己的需求選擇配置即可,本文配置如下:

micro-app

在生成 single-spa 配置后凌摄,我們需要進(jìn)行一些 qiankun 的接入配置羡蛾。我們?cè)?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)如下:

// micro-app-angular/src/main.single-spa.ts
import { enableProdMode, NgZone } from "@angular/core";

import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { Router } from "@angular/router";
import { ?AnimationEngine as AnimationEngine } from "@angular/animations/browser";

import {
  singleSpaAngular,
  getSingleSpaExtraProviders,
} from "single-spa-angular";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";
import { singleSpaPropsSubject } from "./single-spa/single-spa-props";

if (environment.production) {
  enableProdMode();
}

// 微應(yīng)用單獨(dú)啟動(dòng)時(shí)運(yùn)行
if (!(window as any).__POWERED_BY_QIANKUN__) {
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((err) => console.error(err));
}

const { bootstrap, mount, unmount } = singleSpaAngular({
  bootstrapFunction: (singleSpaProps) => {
    singleSpaPropsSubject.next(singleSpaProps);
    return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(
      AppModule
    );
  },
  template: "<app-root />",
  Router,
  NgZone,
  AnimationEngine,
});

/** 主應(yīng)用生命周期鉤子中運(yùn)行 */
export {
  /**
   * bootstrap 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次乾翔,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子爱葵,不會(huì)再重復(fù)觸發(fā) bootstrap。
   * 通常我們可以在這里做一些全局變量的初始化反浓,比如不會(huì)在 unmount 階段被銷毀的應(yīng)用級(jí)別的緩存等萌丈。
   */
  bootstrap,
  /**
   * 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法
   */
  mount,
  /**
   * 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法雷则,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例
   */
  unmount,
};

在配置好了入口文件 main.single-spa.ts 后辆雾,我們還需要配置 webpack,使 main.single-spa.ts 導(dǎo)出的生命周期鉤子函數(shù)可以被 qiankun 識(shí)別獲取月劈。

我們直接配置 extra-webpack.config.js 即可度迂,代碼實(shí)現(xiàn)如下:

// micro-app-angular/extra-webpack.config.js
const singleSpaAngularWebpack = require("single-spa-angular/lib/webpack")
  .default;
const webpackMerge = require("webpack-merge");

module.exports = (angularWebpackConfig, options) => {
  const singleSpaWebpackConfig = singleSpaAngularWebpack(
    angularWebpackConfig,
    options
  );

  const singleSpaConfig = {
    output: {
      // 微應(yīng)用的包名,這里與主應(yīng)用中注冊(cè)的微應(yīng)用名稱一致
      library: "AngularMicroApp",
      // 將你的 library 暴露為所有的模塊定義下都可運(yùn)行的方式
      libraryTarget: "umd",
    },
  };
  const mergedConfig = webpackMerge.smart(
    singleSpaWebpackConfig,
    singleSpaConfig
  );
  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 中的啟動(dòng)命令,修改如下:

// micro-app-angular/package.json
{
  //...
  "script": {
    //...
    // --disable-host-check: 關(guān)閉主機(jī)檢查,使微應(yīng)用可以被 fetch
    // --port: 監(jiān)聽端口
    // --base-href: 站點(diǎn)的起始路徑吭狡,與主應(yīng)用中配置的一致
    "start": "ng serve --disable-host-check --port 10300 --base-href /angular"
  }
}

修改完成后,我們重新啟動(dòng) Angular 微應(yīng)用丈莺,然后打開主應(yīng)用基座 http://localhost:9999划煮。我們點(diǎn)擊左側(cè)菜單切換到微應(yīng)用,此時(shí)我們的 Angular 微應(yīng)用被正確加載啦5薅怼(見下圖)

micro-app

到這里弛秋,Angular 微應(yīng)用就接入成功了!

接入 Jquery俐载、xxx... 微應(yīng)用

這里的 Jquery蟹略、xxx... 微應(yīng)用指的是沒有使用腳手架,直接采用 html + css + js 三劍客開發(fā)的應(yīng)用遏佣。

本案例使用了一些高級(jí) ES 語法挖炬,請(qǐng)使用谷歌瀏覽器運(yùn)行查看效果。

我們以 實(shí)戰(zhàn)案例 - feature-inject-sub-apps 分支 為例状婶,我們?cè)谥鲬?yīng)用的同級(jí)目錄(micro-app-main 同級(jí)目錄)意敛,手動(dòng)創(chuàng)建目錄 micro-app-static

我們使用 express 作為服務(wù)器加載靜態(tài) html膛虫,我們先編輯 package.json草姻,設(shè)置啟動(dòng)命令和相關(guān)依賴。

// micro-app-static/package.json
{
  "name": "micro-app-jquery",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "cors": "^2.8.5"
  },
  "devDependencies": {
    "nodemon": "^2.0.2"
  }
}

然后添加入口文件 index.js稍刀,代碼實(shí)現(xiàn)如下:

// micro-app-static/index.js
const express = require("express");
const cors = require("cors");

const app = express();
// 解決跨域問題
app.use(cors());
app.use('/', express.static('static'));

// 監(jiān)聽端口
app.listen(10400, () => {
  console.log("server is listening in http://localhost:10400")
});

使用 npm install 安裝相關(guān)依賴后撩独,我們使用 npm start 啟動(dòng)應(yīng)用。

我們新建 static 文件夾账月,在文件夾內(nèi)新增一個(gè)靜態(tài)頁面 index.html(代碼在后面會(huì)貼出)综膀,加上一些樣式后,打開瀏覽器捶障,最后效果如下:

micro-app

注冊(cè)微應(yīng)用

在創(chuàng)建好了 Static 微應(yīng)用后僧须,我們可以開始我們的接入工作了。首先我們需要在主應(yīng)用中注冊(cè)該微應(yīng)用的信息项炼,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/micro/apps.ts
const apps = [
  /**
   * 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: "StaticMicroApp",
    entry: "http://localhost:10400",
    container: "#frame",
    activeRule: "/static"
  },
];

export default apps;

通過上面的代碼担平,我們就在主應(yīng)用中注冊(cè)了我們的 Static 微應(yīng)用,進(jìn)入 /static 路由時(shí)將加載我們的 Static 微應(yīng)用锭部。

我們?cè)诓藛闻渲锰幰布尤?Static 微應(yīng)用的快捷入口暂论,代碼實(shí)現(xiàn)如下:

// micro-app-main/src/App.vue
//...
export default class App extends Vue {
  /**
   * 菜單列表
   * key: 唯一 Key 值
   * title: 菜單標(biāo)題
   * path: 菜單對(duì)應(yīng)的路徑
   */
  menus = [
    {
      key: "Home",
      title: "主頁",
      path: "/"
    },
    {
      key: "StaticMicroApp",
      title: "Static 微應(yīng)用",
      path: "/static"
    }
  ];
}

菜單配置完成后,我們的主應(yīng)用基座效果圖如下

micro-app

配置微應(yīng)用

在主應(yīng)用注冊(cè)好了微應(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)用注冊(cè)的生命周期鉤子函數(shù) - bootstrap觉痛。
  • 第 95 行:微應(yīng)用注冊(cè)的生命周期鉤子函數(shù) - mount役衡。
  • 第 102 行:微應(yīng)用注冊(cè)的生命周期鉤子函數(shù) - unmount

完整代碼實(shí)現(xiàn)如下:

<!-- micro-app-static/static/index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <!-- 引入 bootstrap -->
    <link
      
      rel="stylesheet"
    />
    <title>Jquery App</title>
  </head>

  <body>
    <section
      id="jquery-app-container"
      style="padding: 20px; color: blue;"
    ></section>
  </body>
  <!-- 引入 jquery -->
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script>
    /**
     * 請(qǐng)求接口數(shù)據(jù)薪棒,構(gòu)建 HTML
     */
    async function buildHTML() {
      const result = await fetch("http://dev-api.jt-gmall.com/mall", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        // graphql 的查詢風(fēng)格
        body: JSON.stringify({
          query: `{ vegetableList (page: 1, pageSize: 20) { page, pageSize, total, items { _id, name, poster, price } } }`,
        }),
      }).then((res) => res.json());
      const list = result.data.vegetableList.items;
      const html = `<table class="table">
  <thead>
    <tr>
      <th scope="col">菜名</th>
      <th scope="col">圖片</th>
      <th scope="col">報(bào)價(jià)</th>
    </tr>
  </thead>
  <tbody>
    ${list
      .map(
        (item) => `
    <tr>
      <td>
        <img style="width: 40px; height: 40px; border-radius: 100%;" src="${item.poster}"></img>
      </td>
      <td>${item.name}</td>
      <td>¥ ${item.price}</td>
    </tr>
      `
      )
      .join("")}
  </tbody>
</table>`;
      return html;
    }

    /**
     * 渲染函數(shù)
     * 兩種情況:主應(yīng)用生命周期鉤子中運(yùn)行 / 微應(yīng)用單獨(dú)啟動(dòng)時(shí)運(yùn)行
     */
    const render = async ($) => {
      const html = await buildHTML();
      $("#jquery-app-container").html(html);
      return Promise.resolve();
    };

    // 獨(dú)立運(yùn)行時(shí)手蝎,直接掛載應(yīng)用
    if (!window.__POWERED_BY_QIANKUN__) {
      render($);
    }

    ((global) => {
      /**
       * 注冊(cè)微應(yīng)用生命周期鉤子函數(shù)
       * global[appName] 中的 appName 與主應(yīng)用中注冊(cè)的微應(yīng)用名稱一致
       */
      global["StaticMicroApp"] = {
        /**
         * bootstrap 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子俐芯,不會(huì)再重復(fù)觸發(fā) bootstrap棵介。
         * 通常我們可以在這里做一些全局變量的初始化,比如不會(huì)在 unmount 階段被銷毀的應(yīng)用級(jí)別的緩存等吧史。
         */
        bootstrap: () => {
          console.log("MicroJqueryApp bootstraped");
          return Promise.resolve();
        },
        /**
         * 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法邮辽,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法
         */
        mount: () => {
          console.log("MicroJqueryApp mount");
          return render($);
        },
        /**
         * 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例
         */
        unmount: () => {
          console.log("MicroJqueryApp unmount");
          return Promise.resolve();
        },
      };
    })(window);
  </script>
</html>

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

micro-app

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

micro-app

到這里芳肌,Static 微應(yīng)用就接入成功了灵再!

擴(kuò)展閱讀

如果在 Static 微應(yīng)用的 html 中注入 SPA 路由功能的話,將演變成單頁應(yīng)用亿笤,只需要在主應(yīng)用中注冊(cè)一次翎迁。

如果是多個(gè) html 的多頁應(yīng)用 - MPA,則需要在服務(wù)器(或反向代理服務(wù)器)中通過 referer 頭返回對(duì)應(yīng)的 html 文件净薛,或者在主應(yīng)用中注冊(cè)多個(gè)微應(yīng)用(不推薦)汪榔。

小結(jié)

最后,我們所有微應(yīng)用都注冊(cè)在主應(yīng)用和主應(yīng)用的菜單中肃拜,效果圖如下:

micro-app

從上圖可以看出痴腌,我們把不同技術(shù)棧 Vue、React燃领、Angular士聪、Jquery... 的微應(yīng)用都已經(jīng)接入到主應(yīng)用基座中啦!

最后一件事

如果您已經(jīng)看到這里了猛蔽,希望您還是點(diǎn)個(gè) 再走吧~

您的 點(diǎn)贊 是對(duì)作者的最大鼓勵(lì)剥悟,也可以讓更多人看到本篇文章灵寺!

如果感興趣的話,請(qǐng)關(guān)注 博客 或者關(guān)注作者即可獲取最新動(dòng)態(tài)区岗!

github 地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末略板,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子慈缔,更是在濱河造成了極大的恐慌蚯根,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胀糜,死亡現(xiàn)場離奇詭異,居然都是意外死亡蒂誉,警方通過查閱死者的電腦和手機(jī)教藻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來右锨,“玉大人括堤,你說我怎么就攤上這事∩芤疲” “怎么了悄窃?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蹂窖。 經(jīng)常有香客問我轧抗,道長,這世上最難降的妖魔是什么瞬测? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任横媚,我火速辦了婚禮,結(jié)果婚禮上月趟,老公的妹妹穿的比我還像新娘灯蝴。我一直安慰自己,他們只是感情好孝宗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布穷躁。 她就那樣靜靜地躺著,像睡著了一般因妇。 火紅的嫁衣襯著肌膚如雪问潭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天沙峻,我揣著相機(jī)與錄音睦授,去河邊找鬼。 笑死摔寨,一個(gè)胖子當(dāng)著我的面吹牛去枷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼删顶,長吁一口氣:“原來是場噩夢啊……” “哼竖螃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逗余,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤特咆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后录粱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腻格,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年啥繁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菜职。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旗闽,死狀恐怖酬核,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情适室,我是刑警寧澤嫡意,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站捣辆,受9級(jí)特大地震影響蔬螟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜罪帖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一促煮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧整袁,春花似錦菠齿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炸客,卻和暖如春疾棵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痹仙。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工是尔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人开仰。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓拟枚,卻偏偏與公主長得像薪铜,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恩溅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355