qiankun 使用方法及基座子应用搭建实践
1、介绍
Qiankun 是一个基于single-spa 的微前端实现库,旨在帮助大家能更简单,无痛的构建一个 生产可用微前端架构系统。 孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台,经过一批线上应用的充分检验及 打磨后,将其微前端内核抽离出来并开源。目前 qiankun 已在蚂蚁内部服务了超过 200+ 线 上应用,在易用性及完备性上,是值得信赖的。
2、核心设计理念
微前端的核心目标是将巨石应用拆解成若干可自治的松耦合微应用,而qiankun 的诸多设计均 是秉持这一原则。如:html entry, 沙箱,应用间通信等。确保微应用真正具备独立开发,独 立运行的能力;使得微应用的接入像使用iframe一样简单。
2.1 技术特性
技术栈无关,可支持多种技术框架;
独立开发,独立部署,独立运行;
基于single-spa 封装,提供了更加开箱即用的API;
HTML Entry 接入方式: import-html-entry 去加载对应html 文件;
样式隔离,JS 沙箱:确保微应用之间样式互相不干扰,确保微应用之间 全局变量/事件不冲 突;
资源预加载:在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度;
3、qiankun实战
3.1 架构搭建
qiankun 是基于基座模式的,所以它必然有一个基座应用(主应用),来管理各个子应用的加 载和卸载。我们以一个基于Vue的管理系统为例,来看如何搭建一个微前端项目。
3.2 基座搭建
- qiankun安装:npm i qiankun -S
- 搭建好一个常规的Vue应用,并编写好以上三个区域对应的组件,此时的根组件结构 大概如下
<template>
<div class="container">
<div class="app-header">
<prime-header></prime-header>
</div>
<div class="app-menu">
<prime-menu></prime-menu>
</div>
<div class="app-content">
<div id="container"></div>
</div>
<div class="help-taggle">
<prime-help></prime-menu>
</div>
</div>
</template>
- 创建一个文件registerMicroApps,来定义各个子应用的入口:
// 导入注入应用,初始化数据的方法
import { registerMicroApps, initGlobalState } from "qiankun";
// 定义注入子应用的数组
export const registerList = [
{
name: "vue-child-app",
//package.json 中的名字,唯一不可重复,qiankun 基于此变量进行缓存
entry: "http://XXXX", // 子应用服务地址
container: "#container", // 注入主应用的Id
activeRule: "/portal/****", // 触发子应用路由
},
]
// 导出注入子应用的方法,哪里使用,哪里调用
export function registerMicroAppsData() {
// 定义全局数据
const state = {
changeMainData: true,
userInfo: localStorage.getItem("**"),
};
// 初始化全局数据
const actions = initGlobalState(state);
// 全局数据发生改变时触发的方法
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
// console.log("主应用22" + state, prev);
});
// 设置初始化数据
actions.setGlobalState(state);
actions.offGlobalStateChange();
// 注入子应用
registerMicroApps(registerList);
}
- 创建子应用入口文件并启用qiankun
import { start } from "qiankun";
if (!window.qiankunStarted) {
window.qiankunStarted = true;
// 启动qiankun,并开启预加载
start({ prefetch: true });
}
- 路由配置history模式
const router = new VueRouter({
mode: 'history',
routes
});
- 路由注入
-
{ path: "/portal/", // 子应用路由前面统一标识 component: Layout, hidden: true, children: [ { path: "*", // paasPortal下所有的路径都适用 //导入子应用的入口文件 component: () => import("@/views/PortalContainer.vue"), name: "portal", title: "portal", meta: { title: "portal", icon: "", affix: false }, // affix 属性与tagView 标签功能相关,true一直固定,false 可以关闭 }, ], },
3.3 基座搭建完成
基座应用已经搭建好了。以history模式为例,根据 qiankun 和 single-spa 的实现原理, 当我们点击菜单的时候,我们可能会通过以下两种方式修改地址栏的地址:
window.history.pushState({}, '', '/portal/***');
window.location.href = 'https://blog.csdn.net/portal/***';
这两种方式都会触发 hashChange 事件,而 qiankun 借助 single-spa 所绑定的监听事件,会侦 测到地址变化,从而执行 single-spa 的 reroute 函数;随后 qiankun 通过 import-html-entry 提供的能力,去下载 /portal/***对应的html文件,并从中解析出所依赖的脚本和 样式文件。 qiankun 会将其封装在一个沙箱中,执行这些脚本并添加样式表,从而渲染出子应 用。经过这些步骤,应用 /portal/*** 就会像一个原生的组件一样被渲染到基座应 用的占位节点内。 当点击其他菜单时,同样会触发 hashChange 事件, single-spa 会重新执行 reroute ,卸载原 子应用,加载新的子应用。
3.4 子应用搭建
- 我们希望子应用可以像原生组件一样直接渲染到基座应用的 content 内容区域,那么按照 single-spa 应用入口协议,我们必须在子应用main.js内暴露出三个必要的生命周期钩子函 数:
let instance = null;
// 在 src 目录新增 public-path.js :
function render(props = {}) {
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? "/portal/***/" : "/",
mode: "history",
routes
});
instance = new Vue({
router,
store,
render: (h) => h(App)
}).$mount(container ? container.querySelector("#app") : "#app");
}
// 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,
// 不会再重复触发 bootstrap
export async function bootstrap() {
console.log("[vue] vue app bootstraped");
}
// 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
export async function mount(props) {
console.log("[vue] props from main framework", props);
console.log("通过主应用props传过来的值", props.text123);
// 监听数据 写在vue原型链上,方便全局调用
Vue.prototype.$onGlobalStateChange = props.onGlobalStateChange;
Vue.prototype.$setGlobalState = props.setGlobalState;
Vue.prototype.$onGlobalStateChange((state, prev) => {
console.log("通过主应用initGlobalState传过来的值", state, prev);
});
// 设置数据,需要更改全局数据中的一个方法才能触发onGlobalStateChange方法
Vue.prototype.$setGlobalState({
changeMainData: true;// 此属性为在全局设置的一个无效属性,可拿到全局数据
});
Vue.prototype.$text123 = props.text123;
Vue.prototype.$adminToken = props.adminToken;
render(props);
}
export async function unmount() {
// 实例销毁,与路由同时设置为空
instance.$destroy();
instance.$el.innerHTML = "";
instance = null;
router = null;
}
- 在 src 目录新增 public-path.js :
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
- 打包配置修改( vue.config.js ):
// umd格式的应用会向外暴露指定的生命周期钩子函数,便于single-spa解析。
//jsonpFunction配置是webpack打包之后保存在window中的key,
// 只需保证各个子应用不一致即可。
const { name } = require('./package');
module.exports = {
devServer: {
headers: {
'Access-Control-Allow-Origin': '*', // 跨域配置
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${name}`,
},
},
};
- 路由配置
router = new VueRouter({
// 路由前面统一添加path和基座中注入子应用的activeRule 相对应
base: window.__POWERED_BY_QIANKUN__ ? "/portal/***/" : "/",
mode: "history",
routes
});
- 完成以上配置后,分别启动基座应用和子应用。当访问基座应用并点击概况菜单时,地址栏的 url会发生变化, qiankun 就会自动去子应用所在的服务下载html文件,并解析出脚本和样式 进行渲染,于是这个子应用就会展示在图中的 content 内容区域了。 qiankun 搭建完成。
4、总结
4.1 常见问题(详情如上述)
- 全局传值问题
- 注入应用名称问题
- 路由触发规则
4.1 参考官网
本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:licqi@yunshuaiweb.com
上一篇:vite+vue3中require is not defined
下一篇:[Vue warn]: Avoid adding reactive properties to a Vue instance or its root $data at runtime - declar