基于 qiankun 的微前端最佳实践(万字长文) - 从 0 到 1 篇
"display: none;">"display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">引言"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
大家好~
本文是基于 qiankun
的微前端最佳实践系列文章之 从 0 到 1 篇
,本文将分享如何使用 qiankun
如何搭建主应用基座,然后接入不同技术栈的微应用,完成微前端架构的从 0 到 1。
本教程采用 Vue
作为主应用基座,接入不同技术栈的微应用。如果你不懂 Vue
也没关系,我们在搭建主应用基座的教程尽量不涉及 Vue
的 API
,涉及到 API
的地方都会给出解释。
注意:
qiankun
属于无侵入性的微前端框架,对主应用基座和微应用的技术栈都没有要求。
我们在本教程中,接入了多技术栈 微应用
的 主应用
最终效果图如下:
"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;">
我们以 实战案例 - feature-inject-sub-apps 分支 (案例是以 Vue
为基座的主应用,接入多个微应用) 为例,来介绍一下如何在 qiankun
中如何接入不同技术栈的微应用。
我们先使用 vue-cli
生成一个 Vue
的项目,初始化主应用。
vue-cli 是
Vue
官方提供的脚手架工具,用于快速搭建一个Vue
项目。如果你想跳过这一步,可以直接clone
实战案例 - feature-inject-sub-apps 分支 的代码。
将普通的项目改造成 qiankun
主应用基座,需要进行三步操作:
创建微应用容器 - 用于承载微应用,渲染显示微应用; 注册微应用 - 设置微应用激活条件,微应用地址等等; 启动 qiankun
;
"display: none;">创建微应用容器"display: none;">
我们先在主应用中创建微应用的承载容器,这个容器规定了微应用的显示区域,微应用将在该容器内渲染并显示。
我们先设置路由,路由文件规定了主应用自身的路由匹配规则,代码实现如下:
"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: 路径为 / 时触发该路由规则
* name: 路由的 name 为 Home
* component: 触发路由时加载 `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;">/**
* 注册路由实例
* 即将开始监听 location 变化,触发路由规则
*/
"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;">// 创建 Vue 实例
"color: #5c6370;font-style: italic;line-height: 26px;">// 该实例将挂载/渲染在 id 为 main-app 的节点上
"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");
从上面代码可以看出,我们设置了主应用的路由规则,设置了 Home
主页的路由匹配规则。
我们现在来设置主应用的布局,我们会有一个菜单和显示区域,代码实现如下:
"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: 菜单标题
* path: 菜单对应的路径
*/
menus = [
{
key: "color: #98c379;line-height: 26px;">"Home",
title: "color: #98c379;line-height: 26px;">"主页",
path: "color: #98c379;line-height: 26px;">"/",
},
];
}
上面的代码是我们对菜单配置的实现,我们还需要实现基座和微应用的显示区域(如下图)
我们来分析一下上面的代码:
第 5 行
:主应用菜单,用于渲染菜单;第 9 行
:主应用渲染区。在触发主应用路由规则时(由路由配置表的$route.name
判断),将渲染主应用的组件;第 10 行
:微应用渲染区。在未触发主应用路由规则时(由路由配置表的$route.name
判断),将渲染微应用节点;
从上面的分析可以看出,我们使用了在路由表配置的 name
字段进行判断,判断当前路由是否为主应用路由,最后决定渲染主应用组件或是微应用节点。
由于篇幅原因,样式实现代码就不贴出来了,最后主应用的实现效果如下图所示:
从上图可以看出,我们主应用的组件和微应用是显示在同一片内容区域,根据路由规则决定渲染规则。
"display: none;">注册微应用"display: none;">
在构建好了主框架后,我们需要使用 qiankun
的 registerMicroApps
方法注册微应用,代码实现如下:
"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;">// 此时我们还没有微应用,所以 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;">// 一个进度条插件
"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;">// 微应用注册信息
"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;">/**
* 注册微应用
* 第一个参数 - 微应用的注册信息
* 第二个参数 - 全局生命周期钩子
*/
registerMicroApps(apps, {
"color: #5c6370;font-style: italic;line-height: 26px;">// qiankun 生命周期钩子 - 微应用加载前
beforeLoad: "line-height: 26px;">("line-height: 26px;">app: "color: #e6c07b;line-height: 26px;">any) => {
"color: #5c6370;font-style: italic;line-height: 26px;">// 加载微应用前,加载进度条
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 生命周期钩子 - 微应用挂载后
afterMount: "line-height: 26px;">("line-height: 26px;">app: "color: #e6c07b;line-height: 26px;">any) => {
"color: #5c6370;font-style: italic;line-height: 26px;">// 加载微应用前,进度条加载完成
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;">// 加载失败时提示
"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;">"微应用加载失败,请检查应用是否可运行");
}
});
"color: #5c6370;font-style: italic;line-height: 26px;">// 导出 qiankun 的启动函数
"color: #c678dd;line-height: 26px;">export "color: #c678dd;line-height: 26px;">default start;
从上面可以看出,我们的微应用注册信息在 apps
数组中(此时为空,我们在后面接入微应用时会添加微应用注册信息),然后使用 qiankun
的 registerMicroApps
方法注册微应用,最后导出了 start
函数,注册微应用的工作就完成啦!
"display: none;">启动主应用"display: none;">
我们在注册好了微应用,导出 start
函数后,我们需要在合适的地方调用 start
启动主应用。
我们一般是在入口文件启动 qiankun
主应用,代码实现如下:
"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();
最后,启动我们的主应用,效果图如下:
因为我们还没有注册任何微应用,所以这里的效果图和上面的效果图是一样的。
到这一步,我们的主应用基座就创建好啦!
"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
内部通过 import-entry-html
加载微应用,要求微应用需要导出生命周期钩子函数(见下图)。
从上图可以看出,qiankun
内部会校验微应用的生命周期钩子函数,如果微应用没有导出这三个生命周期钩子函数,则微应用会加载失败。
如果我们使用了脚手架搭建微应用的话,我们可以通过 webpack
配置在入口文件处导出这三个生命周期钩子函数。如果没有使用脚手架的话,也可以直接在微应用的 window
上挂载这三个生命周期钩子函数。
现在我们来接入我们的各个技术栈微应用吧!
注意,下面的内容对相关技术栈
API
不会再有过多介绍啦,如果你要接入不同技术栈的微应用,最好要对该技术栈有一些基础了解。
"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
微应用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
我们以 实战案例 - feature-inject-sub-apps 分支 为例,我们在主应用的同级目录(micro-app-main
同级目录),使用 vue-cli
先创建一个 Vue
的项目,在命令行运行如下命令:
"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
选项如下图所示,你也可以根据自己的喜好选择配置。
在新建项目完成后,我们创建几个路由页面再加上一些样式,最后效果如下:
"display: none;">注册微应用"display: none;">
在创建好了 Vue
微应用后,我们可以开始我们的接入工作了。首先我们需要在主应用中注册该微应用的信息,代码实现如下:
"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: 微应用名称 - 具有唯一性
* entry: 微应用入口 - 通过该地址加载微应用
* container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
*/
{
name: "color: #98c379;line-height: 26px;">"VueMicroApp",
entry: "color: #98c379;line-height: 26px;">"//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;
通过上面的代码,我们就在主应用中注册了我们的 Vue
微应用,进入 /vue
路由时将加载我们的 Vue
微应用。
我们在菜单配置处也加入 Vue
微应用的快捷入口,代码实现如下:
"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: 菜单标题
* path: 菜单对应的路径
*/
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",
},
];
}
菜单配置完成后,我们的主应用基座效果图如下
"display: none;">配置微应用"display: none;">
在主应用注册好了微应用后,我们还需要对微应用进行一系列的配置。首先,我们在 Vue
的入口文件 main.js
中,导出 qiankun
主应用所需要的三个生命周期钩子函数,代码实现如下:
从上图来分析:
第 6 行
:webpack
默认的publicPath
为""
空字符串,会基于当前路径来加载资源。我们在主应用中加载微应用时需要重新设置publicPath
,这样才能正确加载微应用的相关资源。(public-path.js
具体实现在后面)第 21 行
:微应用的挂载函数,在主应用中运行时将在mount
生命周期钩子函数中调用,可以保证在沙箱内运行。第 38 行
:微应用独立运行时,直接执行render
函数挂载微应用。第 46 行
:微应用导出的生命周期钩子函数 -bootstrap
。第 53 行
:微应用导出的生命周期钩子函数 -mount
。第 61 行
:微应用导出的生命周期钩子函数 -unmount
。
完整代码实现如下:
"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;">// 动态设置 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;">/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
"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 中创建 VueRouter,可以保证在卸载微应用时,移除 location 事件监听,防止事件污染
router = "color: #c678dd;line-height: 26px;">new VueRouter({
"color: #5c6370;font-style: italic;line-height: 26px;">// 运行在主应用中时,添加路由命名空间 /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;">// 挂载应用
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;">// 独立运行时,直接挂载应用
"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 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
"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;">/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
"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;">/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
"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
导出的生命周期钩子函数可以被 qiankun
识别获取。
我们直接配置 vue.config.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-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;">// 监听端口
"color: #d19a66;line-height: 26px;">port: "color: #d19a66;line-height: 26px;">10200,
"color: #5c6370;font-style: italic;line-height: 26px;">// 关闭主机检查,使微应用可以被 fetch
"color: #d19a66;line-height: 26px;">disableHostCheck: "color: #56b6c2;line-height: 26px;">true,
"color: #5c6370;font-style: italic;line-height: 26px;">// 配置跨域请求头,解决开发环境的跨域问题
"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;">// 微应用的包名,这里与主应用中注册的微应用名称一致
"color: #d19a66;line-height: 26px;">library: "color: #98c379;line-height: 26px;">"VueMicroApp",
"color: #5c6370;font-style: italic;line-height: 26px;">// 将你的 library 暴露为所有的模块定义下都可运行的方式
"color: #d19a66;line-height: 26px;">libraryTarget: "color: #98c379;line-height: 26px;">"umd",
"color: #5c6370;font-style: italic;line-height: 26px;">// 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可
"color: #d19a66;line-height: 26px;">jsonpFunction: "color: #98c379;line-height: 26px;">`webpackJsonp_VueMicroApp`,
},
},
};
我们需要重点关注一下 output
选项,当我们把 libraryTarget
设置为 umd
后,我们的 library
就暴露为所有的模块定义下都可运行的方式了,主应用就可以获取到微应用的生命周期钩子函数了。
在 vue.config.js
修改完成后,我们重新启动 Vue
微应用,然后打开主应用基座 http://localhost:9999
。我们点击左侧菜单切换到微应用,此时我们的 Vue
微应用被正确加载啦!(见下图)
此时我们打开控制台,可以看到我们所执行的生命周期钩子函数(见下图)
到这里,Vue
微应用就接入成功了!
"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
微应用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
我们以 实战案例 - feature-inject-sub-apps 分支 为例,我们在主应用的同级目录(micro-app-main
同级目录),使用 react-create-app
先创建一个 React
的项目,在命令行运行如下命令:
"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
在项目创建完成后,我们在根目录下添加 .env
文件,设置项目监听的端口,代码实现如下:
"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
然后,我们创建几个路由页面再加上一些样式,最后效果如下:
"display: none;">注册微应用"display: none;">
在创建好了 React
微应用后,我们可以开始我们的接入工作了。首先我们需要在主应用中注册该微应用的信息,代码实现如下:
"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: 微应用名称 - 具有唯一性
* entry: 微应用入口 - 通过该地址加载微应用
* container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
*/
{
name: "color: #98c379;line-height: 26px;">"ReactMicroApp",
entry: "color: #98c379;line-height: 26px;">"//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;
通过上面的代码,我们就在主应用中注册了我们的 React
微应用,进入 /react
路由时将加载我们的 React
微应用。
我们在菜单配置处也加入 React
微应用的快捷入口,代码实现如下:
"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: 菜单标题
* path: 菜单对应的路径
*/
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",
},
];
}
菜单配置完成后,我们的主应用基座效果图如下
"display: none;">配置微应用"display: none;">
在主应用注册好了微应用后,我们还需要对微应用进行一系列的配置。首先,我们在 React
的入口文件 index.js
中,导出 qiankun
主应用所需要的三个生命周期钩子函数,代码实现如下:
从上图来分析:
第 5 行
:webpack
默认的publicPath
为""
空字符串,会基于当前路径来加载资源。我们在主应用中加载微应用时需要重新设置publicPath
,这样才能正确加载微应用的相关资源。(public-path.js
具体实现在后面)第 12 行
:微应用的挂载函数,在主应用中运行时将在mount
生命周期钩子函数中调用,可以保证在沙箱内运行。第 17 行
:微应用独立运行时,直接执行render
函数挂载微应用。第 25 行
:微应用导出的生命周期钩子函数 -bootstrap
。第 32 行
:微应用导出的生命周期钩子函数 -mount
。第 40 行
:微应用导出的生命周期钩子函数 -unmount
。
完整代码实现如下:
"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;">// 动态设置 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;">/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
"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;">// 独立运行时,直接挂载应用
"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 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
"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;">/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
"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;">/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
"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
后,我们还需要配置路由命名空间,以确保主应用可以正确加载微应用,代码实现如下:
"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;">// 设置路由命名空间
"line-height: 26px;">"line-height: 26px;"><"color: #e06c75;line-height: 26px;">Router "color: #d19a66;line-height: 26px;">basename="color: #98c379;line-height: 26px;">{BASE_NAME}>{/* ... */}"line-height: 26px;">"color: #e06c75;line-height: 26px;">Router>
);
};
接下来,我们还需要配置 webpack
,使 index.js
导出的生命周期钩子函数可以被 qiankun
识别获取。
我们需要借助 react-app-rewired
来帮助我们修改 webpack
的配置,我们直接安装该插件:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">npm install react-app-rewired -D
在 react-app-rewired
安装完成后,我们还需要修改 package.json
的 scripts
选项,修改为由 react-app-rewired
启动应用,就像下面这样
"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
,代码实现如下:
"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;">// 微应用的包名,这里与主应用中注册的微应用名称一致
config.output.library = "color: #98c379;line-height: 26px;">`ReactMicroApp`;
"color: #5c6370;font-style: italic;line-height: 26px;">// 将你的 library 暴露为所有的模块定义下都可运行的方式
config.output.libraryTarget = "color: #98c379;line-height: 26px;">"umd";
"color: #5c6370;font-style: italic;line-height: 26px;">// 按需加载相关,设置为 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;">// 关闭主机检查,使微应用可以被 fetch
config.disableHostCheck = "color: #56b6c2;line-height: 26px;">true;
"color: #5c6370;font-style: italic;line-height: 26px;">// 配置跨域请求头,解决开发环境的跨域问题
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;
};
},
};
我们需要重点关注一下 output
选项,当我们把 libraryTarget
设置为 umd
后,我们的 library
就暴露为所有的模块定义下都可运行的方式了,主应用就可以获取到微应用的生命周期钩子函数了。
在 config-overrides.js
修改完成后,我们重新启动 React
微应用,然后打开主应用基座 http://localhost:9999
。我们点击左侧菜单切换到微应用,此时我们的 React
微应用被正确加载啦!(见下图)
此时我们打开控制台,可以看到我们所执行的生命周期钩子函数(见下图)
到这里,React
微应用就接入成功了!
"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
微应用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
Angular
与 qiankun
目前的兼容性并不太好,接入 Angular
微应用需要一定的耐心与技巧。
对于选择
Angular
技术栈的前端开发来说,对这类情况应该驾轻就熟(没有办法)。
我们以 实战案例 - feature-inject-sub-apps 分支 为例,我们在主应用的同级目录(micro-app-main
同级目录),使用 @angular/cli
先创建一个 Angular
的项目,在命令行运行如下命令:
"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
选项如下图所示,你也可以根据自己的喜好选择配置。
然后,我们创建几个路由页面再加上一些样式,最后效果如下:
"display: none;">注册微应用"display: none;">
在创建好了 Angular
微应用后,我们可以开始我们的接入工作了。首先我们需要在主应用中注册该微应用的信息,代码实现如下:
"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: 微应用名称 - 具有唯一性
* entry: 微应用入口 - 通过该地址加载微应用
* container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
*/
{
name: "color: #98c379;line-height: 26px;">"AngularMicroApp",
entry: "color: #98c379;line-height: 26px;">"//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;
通过上面的代码,我们就在主应用中注册了我们的 Angular
微应用,进入 /angular
路由时将加载我们的 Angular
微应用。
我们在菜单配置处也加入 Angular
微应用的快捷入口,代码实现如下:
"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: 菜单标题
* path: 菜单对应的路径
*/
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",
},
];
}
菜单配置完成后,我们的主应用基座效果图如下
最后我们在主应用的入口文件,引入 zone.js
,代码实现如下:
Angular
运行依赖于zone.js
。
qiankun
基于single-spa
实现,single-spa
明确指出一个项目的zone.js
只能存在一份实例,所以我们在主应用注入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 微应用所做的 zone 包注入
"color: #c678dd;line-height: 26px;">import "color: #98c379;line-height: 26px;">"zone.js/dist/zone";
"display: none;">配置微应用"display: none;">
在主应用的工作完成后,我们还需要对微应用进行一系列的配置。首先,我们使用 single-spa-angular
生成一套配置,在命令行运行以下命令:
"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
运行命令时,根据自己的需求选择配置即可,本文配置如下:
在生成 single-spa
配置后,我们需要进行一些 qiankun
的接入配置。我们在 Angular
微应用的入口文件 main.single-spa.ts
中,导出 qiankun
主应用所需要的三个生命周期钩子函数,代码实现如下:
从上图来分析:
第 21 行
:微应用独立运行时,直接执行挂载函数挂载微应用。第 46 行
:微应用导出的生命周期钩子函数 -bootstrap
。第 50 行
:微应用导出的生命周期钩子函数 -mount
。第 54 行
:微应用导出的生命周期钩子函数 -unmount
。
完整代码实现如下:
"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;">// 微应用单独启动时运行
"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;">/** 主应用生命周期钩子中运行 */
"color: #c678dd;line-height: 26px;">export {
"color: #5c6370;font-style: italic;line-height: 26px;">/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
bootstrap,
"color: #5c6370;font-style: italic;line-height: 26px;">/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
mount,
"color: #5c6370;font-style: italic;line-height: 26px;">/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
unmount,
};
在配置好了入口文件 main.single-spa.ts
后,我们还需要配置 webpack
,使 main.single-spa.ts
导出的生命周期钩子函数可以被 qiankun
识别获取。
我们直接配置 extra-webpack.config.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-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;">// 微应用的包名,这里与主应用中注册的微应用名称一致
"color: #d19a66;line-height: 26px;">library: "color: #98c379;line-height: 26px;">"AngularMicroApp",
"color: #5c6370;font-style: italic;line-height: 26px;">// 将你的 library 暴露为所有的模块定义下都可运行的方式
"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;
};
我们需要重点关注一下 output
选项,当我们把 libraryTarget
设置为 umd
后,我们的 library
就暴露为所有的模块定义下都可运行的方式了,主应用就可以获取到微应用的生命周期钩子函数了。
在 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: 关闭主机检查,使微应用可以被 fetch
"color: #5c6370;font-style: italic;line-height: 26px;">// --port: 监听端口
"color: #5c6370;font-style: italic;line-height: 26px;">// --base-href: 站点的起始路径,与主应用中配置的一致
"color: #d19a66;line-height: 26px;">"start": "color: #98c379;line-height: 26px;">"ng serve --disable-host-check --port 10300 --base-href /angular"
}
}
修改完成后,我们重新启动 Angular
微应用,然后打开主应用基座 http://localhost:9999
。我们点击左侧菜单切换到微应用,此时我们的 Angular
微应用被正确加载啦!(见下图)
到这里,Angular
微应用就接入成功了!
"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...
微应用"display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;">
这里的
Jquery、xxx...
微应用指的是没有使用脚手架,直接采用html + css + js
三剑客开发的应用。本案例使用了一些高级
ES
语法,请使用谷歌浏览器运行查看效果。
我们以 实战案例 - feature-inject-sub-apps 分支 为例,我们在主应用的同级目录(micro-app-main
同级目录),手动创建目录 micro-app-static
。
我们使用 express
作为服务器加载静态 html
,我们先编辑 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-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
,代码实现如下:
"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;">// 监听端口
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
安装相关依赖后,我们使用 npm start
启动应用。
我们新建 static
文件夹,在文件夹内新增一个静态页面 index.html
(代码在后面会贴出),加上一些样式后,打开浏览器,最后效果如下:
"display: none;">注册微应用"display: none;">
在创建好了 Static
微应用后,我们可以开始我们的接入工作了。首先我们需要在主应用中注册该微应用的信息,代码实现如下:
"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: 微应用名称 - 具有唯一性
* entry: 微应用入口 - 通过该地址加载微应用
* container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
*/
{
name: "color: #98c379;line-height: 26px;">"StaticMicroApp",
entry: "color: #98c379;line-height: 26px;">"//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;
通过上面的代码,我们就在主应用中注册了我们的 Static
微应用,进入 /static
路由时将加载我们的 Static
微应用。
我们在菜单配置处也加入 Static
微应用的快捷入口,代码实现如下:
"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: 菜单标题
* path: 菜单对应的路径
*/
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 微应用",
path: "color: #98c379;line-height: 26px;">"/static"
}
];
}
菜单配置完成后,我们的主应用基座效果图如下
"display: none;">配置微应用"display: none;">
在主应用注册好了微应用后,我们还需要直接写微应用 index.html
的代码即可,代码实现如下:
从上图来分析:
第 70 行
:微应用的挂载函数,在主应用中运行时将在mount
生命周期钩子函数中调用,可以保证在沙箱内运行。第 77 行
:微应用独立运行时,直接执行render
函数挂载微应用。第 88 行
:微应用注册的生命周期钩子函数 -bootstrap
。第 95 行
:微应用注册的生命周期钩子函数 -mount
。第 102 行
:微应用注册的生命周期钩子函数 -unmount
。
完整代码实现如下:
"display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/ygUAW1Il7aS8BSBksFIEPz9IB2UicG4Ct6RicruHb0wP4gFDvpOWVqr6CPAucpFQPsibNBzHmxpBtDicK6VMZv7tFA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">"color: #5c6370;font-style: italic;line-height: 26px;">
"color: #61aeee;line-height: 26px;">html>
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">html "color: #d19a66;line-height: 26px;">lang="color: #98c379;line-height: 26px;">"en">
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">head>
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">meta "color: #d19a66;line-height: 26px;">charset="color: #98c379;line-height: 26px;">"UTF-8" />
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">meta "color: #d19a66;line-height: 26px;">name="color: #98c379;line-height: 26px;">"viewport" "color: #d19a66;line-height: 26px;">content="color: #98c379;line-height: 26px;">"width=device-width, initial-scale=1.0" />
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">meta "color: #d19a66;line-height: 26px;">http-equiv="color: #98c379;line-height: 26px;">"X-UA-Compatible" "color: #d19a66;line-height: 26px;">content="color: #98c379;line-height: 26px;">"ie=edge" />
"color: #5c6370;font-style: italic;line-height: 26px;">
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">link
"color: #d19a66;line-height: 26px;">href="color: #98c379;line-height: 26px;">"https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"
"color: #d19a66;line-height: 26px;">rel="color: #98c379;line-height: 26px;">"stylesheet"
/>
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">title>Jquery App"line-height: 26px;">"color: #e06c75;line-height: 26px;">title>
"line-height: 26px;">"color: #e06c75;line-height: 26px;">head>
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">body>
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">section
"color: #d19a66;line-height: 26px;">id="color: #98c379;line-height: 26px;">"jquery-app-container"
"color: #d19a66;line-height: 26px;">style="color: #98c379;line-height: 26px;">"padding: 20px; color: blue;"
>"line-height: 26px;">"color: #e06c75;line-height: 26px;">section>
"line-height: 26px;">"color: #e06c75;line-height: 26px;">body>
"color: #5c6370;font-style: italic;line-height: 26px;">
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">script "color: #d19a66;line-height: 26px;">src="color: #98c379;line-height: 26px;">"https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js">"line-height: 26px;">"color: #e06c75;line-height: 26px;">script>
"line-height: 26px;"><"color: #e06c75;line-height: 26px;">script>"line-height: 26px;">
"color: #5c6370;font-style: italic;line-height: 26px;">/**
* 请求接口数据,构建 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 的查询风格
"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;">`
菜名
图片
报价
"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;">/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
"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;">// 独立运行时,直接挂载应用
"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;">/**
* 注册微应用生命周期钩子函数
* global[appName] 中的 appName 与主应用中注册的微应用名称一致
*/
global["color: #98c379;line-height: 26px;">"StaticMicroApp"] = {
"color: #5c6370;font-style: italic;line-height: 26px;">/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
"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;">/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
"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;">/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
"color: #d19a66;line-height: 26px;">unmount: "line-height: 26px;">"line-height: 26px;">() => {
"color: #e6c07b;line-height: 26px;">console.log("color: #98c379;line-height: 26px;">"MicroJqueryApp unmount");
"color: #c678dd;line-height: 26px;">return "color: #e6c07b;line-height: 26px;">Promise.resolve();
},
};
})("color: #e6c07b;line-height: 26px;">window);
"line-height: 26px;">"color: #e06c75;line-height: 26px;">script>
"line-height: 26px;">"color: #e06c75;line-height: 26px;">html>
在构建好了 Static
微应用后,我们打开主应用基座 http://localhost:9999
。我们点击左侧菜单切换到微应用,此时可以看到,我们的 Static
微应用被正确加载啦!(见下图)
此时我们打开控制台,可以看到我们所执行的生命周期钩子函数(见下图)
到这里,Static
微应用就接入成功了!
"display: none;">扩展阅读"display: none;">
如果在 Static
微应用的 html
中注入 SPA
路由功能的话,将演变成单页应用,只需要在主应用中注册一次。
如果是多个 html
的多页应用 - MPA
,则需要在服务器(或反向代理服务器)中通过 referer
头返回对应的 html
文件,或者在主应用中注册多个微应用(不推荐)。
"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;">
最后,我们所有微应用都注册在主应用和主应用的菜单中,效果图如下:
从上图可以看出,我们把不同技术栈 Vue、React、Angular、Jquery...
的微应用都已经接入到主应用基座中啦!
"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.看到这里了就点个在看支持下吧,你的「点赞,在看」是我创作的动力。
2.关注公众号
程序员成长指北
,回复「1」加入Node进阶交流群!「在这里有好多 Node 开发者,会讨论 Node 知识,互相学习」!3.也可添加微信【ikoala520】,一起成长。
“在看转发”是最大的支持