微前端二:qiankun

qiankun是基于Single-spa开发的框架,所以我们先来看下Single-spa是怎么做的:

Single-spa 是最早的微前端框架,兼容多种前端技术栈,是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架;

优点:
1、敏捷性 - 独立开发、独立部署,微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新;
2、技术栈无关,主框架不限制接入应用的技术栈,微应用具备完全自主权;
3、增量升级,在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
4、更快交付客户价值,有助于持续集成、持续部署以及持续交付;
5、维护和 bugfix 非常简单,每个团队都熟悉所维护特定的区域;

缺点:
1、无通信机制
2、不支持 Javascript 沙箱
3、样式冲突
4、无法预加载

Single-spa实现原理
首先在基座应用中注册所有App的路由,single-spa保存各子应用的路由映射关系,充当微前端控制器Controler。URL响应时,匹配子应用路由并加载渲染子应用。

基座配置

//main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import { registerApplication, start } from 'single-spa'Vue.config.productionTip = falseconst mountApp = (url) => {return new Promise((resolve, reject) => {const script = document.createElement('script')script.src = urlscript.onload = resolvescript.onerror = reject// 通过插入script标签的方式挂载子应用const firstScript = document.getElementsByTagName('script')[]// 挂载子应用firstScript.parentNode.insertBefore(script, firstScript)})
}const loadApp = (appRouter, appName) => {// 远程加载子应用return async () => {//手动挂载子应用await mountApp(appRouter + '/js/chunk-vendors.js')await mountApp(appRouter + '/js/app.js')// 获取子应用生命周期函数return window[appName]}
}// 子应用列表
const appList = [{// 子应用名称name: 'app1',// 挂载子应用app: loadApp('http://localhost:8083', 'app1'),// 匹配该子路由的条件activeWhen: location => location.pathname.startsWith('/app1'),// 传递给子应用的对象customProps: {}},{name: 'app2',app: loadApp('http://localhost:8082', 'app2'),activeWhen: location => location.pathname.startsWith('/app2'),customProps: {}}
]// 注册子应用
appList.map(item => {registerApplication(item)
})// 注册路由并启动基座
new Vue({router,mounted() {start()},render: h => h(App)
}).$mount('#app')

构建基座的核心是:配置子应用信息,通过registerApplication注册子应用,在基座工程挂载阶段start启动基座。
我们通过代码也发现 Single-spa 是通过插入script标签的方式挂载子应用

子应用配置

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import singleSpaVue from 'single-spa-vue'Vue.config.productionTip = falseconst appOptions = {el: '#microApp',router,render: h => h(App)
}// 支持应用独立运行、部署,不依赖于基座应用
// 如果不是微应用环境,即启动自身挂载的方式
if (!process.env.isMicro) {delete appOptions.elnew Vue(appOptions).$mount('#app')
}
// 基于基座应用,导出生命周期函数
const appLifecycle = singleSpaVue({Vue,appOptions
})// 抛出子应用生命周期
// 启动生命周期函数
export const bootstrap = (props)  => {console.log('app2 bootstrap')return appLifecycle.bootstrap(() => { })
}
// 挂载生命周期函数
export const mount = (props) => {console.log('app2 mount')return appLifecycle.mount(() => { })
}
// 卸载生命周期函数
export const unmount = (props) => {console.log('app2 unmount')return appLifecycle.unmount(() => { })
}

配置子应用为umd打包方式

//vue.config.js
const package = require('./package.json')
module.exports = {// 告诉子应用在这个地址加载静态资源,否则会去基座应用的域名下加载publicPath: '//localhost:8082',// 开发服务器devServer: {port: 8082},configureWebpack: {// 导出umd格式的包,在全局对象上挂载属性package.name,基座应用需要通过这个// 全局对象获取一些信息,比如子应用导出的生命周期函数output: {// library的值在所有子应用中需要library: package.name,libraryTarget: 'umd'}}

子应用配置的核心是用singleSpaVue生成子路由配置后,必须要抛出其生命周期函数。
用以上方式便可轻松实现一个简单的微前端应用了。

那么我们有single-spa这种微前端解决方案,qiankun 又在此基础上做了什么呢

相比于single-spa,qiankun他解决了JS沙盒环境,不需要我们自己去进行处理。在single-spa的开发过程中,我们需要自己手动的去写调用子应用JS的方法(如上面的 createScript方法),而qiankun不需要,乾坤只需要你传入响应的apps的配置即可,会帮助我们去加载。

Qiankun

1、基于 single-spa 封装,提供了更加开箱即用的 API。
2、技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。
3、HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。
4、样式隔离,确保微应用之间样式互相不干扰。
5、JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
6、资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。

基座配置

import { registerMicroApps, start, runAfterFirstMounted, setDefaultMountApp } from 'qiankun';const microApps = [{name: 'reactApp',entry: '//localhost:3000',container: '#container',activeRule: '/app-react',loader: (loading: boolean) => void - 可选,loading 状态发生变化时会调用的方法。props: {} - 可选,主应用需要传递给微应用的数据。},{name: 'vueApp',entry: '//localhost:8080',container: '#container',activeRule: '/app-vue',},{name: 'angularApp',entry: '//localhost:4200',container: '#container',activeRule: '/app-angular',}
]
/**beforeLoad - Lifecycle | Array<Lifecycle> - 可选beforeMount - Lifecycle | Array<Lifecycle> - 可选afterMount - Lifecycle | Array<Lifecycle> - 可选beforeUnmount - Lifecycle | Array<Lifecycle> - 可选afterUnmount - Lifecycle | Array<Lifecycle> - 可选
*/
registerMicroApps(microApps, {beforeLoad: (app) => console.log('before load', app.name),beforeMount: [(app) => console.log('before mount', app.name)],}
);
// 启动 qiankun
start();
// 设置主应用启动后默认进入的微应用。
setDefaultMountApp('/app-react');
// 第一个微应用 mount 后需要调用的方法,比如开启一些监控或者埋点脚本。
runAfterFirstMounted(() => {console.log('第一个微应用已挂在,可以后续操作');
});

子应用配置

// main.ts
function render(props: any = {}) {if (Object.keys(props).length === 0) return;const { userInfo } = props;store.commit('SetGlobalObj', props);store.commit('SetUserInfo', userInfo);instance = new Vue({router,store,render: h => h(App)}).$mount(container ? container.querySelector('#app') : '#app');
}let win:any = window;
// 判断是否是qiankun环境,兼容非微前端环境
if (!win.__POWERED_BY_QIANKUN__) {const router = getRoute('/dashboard/detail/information');new Vue({el: "#app",router,store,render: h => h(App)});
}export async function bootstrap() {console.log('[vue] vue app bootstraped');
}export async function mount(props: any) {render(props);
}export async function unmount() {
}

修改 webpack 配置

这个跟Single-spa 差不多,主要是 把微应用打包成 umd 库格式

const { name } = require('./package');
module.exports = {devServer: {headers: {'Access-Control-Allow-Origin': '*',},},configureWebpack: {output: {library: `${name}-[name]`,// 把微应用打包成 umd 库格式libraryTarget: 'umd', // webpack 5 需要把 jsonpFunction 替换成 chunkLoadingGlobaljsonpFunction: `webpackJsonp_${name}`},},
};

这里特别说明下 qiankun 加载微应用的方式除了上面那种外,如果微应用不是直接跟路由关联的时候,也可以选择手动加载微应用的方式

import { loadMicroApp } from 'qiankun';loadMicroApp({name: 'app',entry: '//localhost:7100',container: '#yourContainer',
});

生命周期钩子注释说明

/*** bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。*/
export async function bootstrap() {console.log('react app bootstraped');
}/*** 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法*/
export async function mount(props) {ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}/*** 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例*/
export async function unmount(props) {ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root') : document.getElementById('root'),);
}/*** 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效*/
export async function update(props) {console.log('update props', props);
}

qiankun的通信方式

1、localStorage/sessionStorage
2、通过路由参数共享
3、官方提供的 props
4、官方提供的 actions
5、使用vuex或redux管理状态,通过shared分享

1、localStorage/sessionStorage

有人说这个方案必须主应用和子应用是同一个域名下。其实不是的,子应用使用不同的域名也是可以的,因为在 qiankun 中,主应用是通过 fetch 来拉取子应用的模板,然后渲染在主应用的 dom 上的,说白了还是运行在主应用上,所以还是运行在同一个域名上,也就是主应用的域名。

父传子
主应用 main.js
localStorage.setItem(‘token’, ‘123’)
console.log(‘在main中设置了token’)

子应用app1 main.js
const token = localStorage.getItem(‘token’)
console.log(‘app1中打印token:’, token)

子传父
同理app1修改token,main也可以看到,这里就不再赘述

2、通过路由参数共享

这个也很好理解,因为只有一个 url,不管子应用还是主应用给 url 上拼接一些参数,那么父子应用都可以通过 route 来获取到。

3、官方提供的 props

这个在前面已经说过了就是registerMicroApps注册微应用时,通过props传送信息

4、官方提供的 actions

就一个 API initGlobalState
qiankun 内部提供了 initGlobalState 方法用于注册 MicroAppStateActions 实例用于通信,该实例有三个方法,分别是:
setGlobalState:设置 globalState - 设置新的值时,内部将执行 浅检查,如果检查到 globalState 发生改变则触发通知,通知到所有的 观察者 函数。
onGlobalStateChange:注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
offGlobalStateChange:取消 观察者 函数 - 该实例不再响应 globalState 变化

在这里插入图片描述

我们从上图可以看出,我们可以先注册 观察者 到观察者池中,然后通过修改 globalState 可以触发所有的 观察者 函数,从而达到组件间通信的效果。

下面就是注册了一个观察者

const actions: MicroAppStateActions = initGlobalState({});
actions.onGlobalStateChange((state, prev) => {// state: 变更后的状态; prev 变更前的状态
});
setTimeout(() => {actions.setGlobalState(Object.assign({ username: 'Lee', obj: { token: 222 } }));
}, 1000);
// actions.offGlobalStateChange();

我们来看具体应用:
父传子
主应用:
actions.js

import { initGlobalState, MicroAppStateActions } from 'qiankun';const state = {num: 1
};// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state);actions.onGlobalStateChange((state, prev) => {// state: 变更后的状态; prev 变更前的状态console.log('主应用检测到state变更:', state, prev);
});// 你还可以定义一个获取state的方法下发到子应用
actions.getGlobalState = function () {return state
}export default actions;

index.js 注册文件

import {registerMicroApps,start,
} from "qiankun";
import actions from './actions.js'
const apps = [{name: "App1MicroApp",entry: '//localhost:9001',container: "#app1",activeRule: "/app1",props: {parentActions: actions}}
];registerMicroApps(apps);
start();

这样就把这个 actions 传给了子应用。
子应用:

let instance = null
let router = nullfunction render (props) {console.log(props.parentActions);// 在子应用中使用就可以访问到这个parentActions了props.parentActions.setGlobalState({ num: 2 })// 调用挂载在 actions 上的自定义方法,获取当前的全局 stateprops.parentActions.getGlobalState();router = new VueRouter({base: '',mode: 'history',routes: routes})new Vue({router,store,render: (h) => h(App)}).$mount('#app')
}export async function mount(props) {render(props);
}

5、shared 方案

就是父应用通过 vuex 或者 redux 正常使用维护一个 state,然后创建一个 shared 实例,这个实例提供对 state 的增删改查,然后通过 props 把这个 shared 实例传给子应用,子应用使用就行。其实和上面挺相似的。
不过可以看出上面4中方案,比较适合各个应用通信比较少的情况,实时上这也是微应用的使用原则-尽量减少他们之间的通信。
但如果我们通过 vuex 或者 redux 正常使用维护一个 state,那可扩展的就多了,也比较适合较多通信的情况。
主应用:
这个 shared 实例大概是这样:

import store from "./store";class Shared {/*** 获取 Username*/public getUsername(): string {const state = store.getState();return state.username || "";}/*** 设置 Username*/public setUsername(token: string): void {// 将 token 的值记录在 store 中store.dispatch({type: "set_username",payload: username});}
}
const shared = new Shared();
export default shared;

同样的传入方式

import {registerMicroApps,start,
} from "qiankun";
import shared from './shared'
const apps = [{name: "App1",entry: '//localhost:9001',container: "#app1",activeRule: "/app1",props: {parentShared: shared}}
];registerMicroApps(apps);
start();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/112950.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

APP应用开发sdk版本过低可能性原因问题排查及解决方案

同学们&#xff0c;在移动 app 开发中&#xff0c;提示sdk版本过低缺找不到原因的情况都知道的吧哈哈哈&#xff0c;这个我觉得我有必要全面的分析和排查&#xff0c;让同学们看完这个文章都得以解决。这是我的初衷奈何地主家里也没有余粮呀&#xff08;我也不能完全总结出来&a…

《动手学深度学习 Pytorch版》 8.7 通过时间反向传播

8.7.1 循环神经网络的梯度分析 本节主要探讨梯度相关问题&#xff0c;因此对模型及其表达式进行了简化&#xff0c;进行如下表示&#xff1a; h t f ( x t , h t − 1 , w h ) o t g ( h t , w o ) \begin{align} h_t&f(x_t,h_{t-1},w_h)\\ o_t&g(h_t,w_o) \end{ali…

用 Python 这样去创建词云不是更美嘛?

什么是词云&#xff1f;在网络上我们经常可以看到一张图片&#xff0c;上面有一大堆大小不一的文字&#xff0c;这便是词云。词云一般是根据输入的大量词语生成的&#xff0c;如果某个词语出现的次数越多&#xff0c;那么相应的大小就会越大。 Python 中有一个专门用来生成词云…

智慧公厕:打造未来城市公共设施的智慧选择

随着信息技术的不断发展&#xff0c;城市公共设施需要实现个性化、高效化和智能化&#xff0c;其中包括公共厕所。智慧公厕是一种结合物联网、互联网、人工智能、云计算、大数据和传感感知等技术的新型公厕&#xff0c;旨在提高公共厕所管理效率、跨区域跨系统的联网管理、自动…

【C++入门 一 】学习C++背景、开启C++奇妙之旅

目录 1.什么是C2. C的发展史3. C的重要性3.1 语言的使用广泛度3.2 在工作领域1. 操作系统以及大型系统软件开发2. 服务器端开发3. 游戏开发4. 嵌入式和物联网领域5. 数字图像处理6. 人工智能7. 分布式应用 3.3 在校招领域3.3.1 岗位需求3.3.2 笔试题 4. 如何学习C4.1 别人怎么学…

WAL 模式(PostgreSQL 14 Internals翻译版)

性能 当服务器正常运行时&#xff0c;WAL文件不断被写入磁盘。但是&#xff0c;这些写操作是顺序的:几乎没有随机访问&#xff0c;因此即使是HDD也可以处理这个任务。由于这种类型的加载与典型的数据文件访问非常不同&#xff0c;因此有必要为WAL文件设置一个单独的物理存储&a…

数据结构之堆

目录 前言 堆的概念与结构 堆的实现 堆的初始化 堆的销毁 堆的显示 堆的插入 堆的向上调整算法 堆的删除 堆的向下调整算法 堆的判空 获取堆顶元素 堆的数据个数 堆的创建 前言 二叉树的顺序结构存储即使用数组存储&#xff0c;而数组存储适用于完全二叉树&#xf…

【具身智能综述1】A Survey of Embodied AI: From Simulators to Research Tasks

论文标题&#xff1a;A Survey of Embodied AI: From Simulators to Research Tasks 论文作者&#xff1a;Jiafei Duan, Samson Yu, Hui Li Tan, Hongyuan Zhu, Cheston Tan 论文原文&#xff1a;https://arxiv.org/abs/2103.04918 论文出处&#xff1a;IEEE Transactions on E…

柯桥银泰附近有学德语的地方吗,留学德语培训

01 die Garantiefr jemandem/etwas 给某人或某事的保障 Das System bietet die Garantie fr die Bauer. 02 der Gebrauch von etwas 使用某物 Wir haben den Gebrauch vom Computer gelerbt. 我们学会了使用电脑。 03 Die Geduld mit jemandem/etwas 对..的耐心 Der Lehre…

智慧河湖方案:AI赋能水利水务,构建河湖智能可视化监管大数据平台

一、方案背景 我国江河湖泊众多&#xff0c;水系发达。伴随着经济社会快速发展&#xff0c;水生态水环境问题成为群众最关注的民生议题之一。一些河流开发利用已接近甚至超出水环境承载能力&#xff0c;一些地区废污水排放量居高不下&#xff0c;一些地方侵占河道、围垦湖泊等…

如何在不恢复出厂设置的情况下解锁 Android 手机密码?

如何在不恢复出厂设置的情况下解锁 Android 手机密码&#xff1f; 当您忘记 Android 手机的密码时&#xff0c;可能会有压力&#xff0c;尤其是当您不想恢复出厂设置并删除所有数据时。但是&#xff0c;有一些方法可以在不诉诸如此激烈的步骤的情况下解锁手机。我们将在这篇文…

数据仓库扫盲系列(1):数据仓库诞生原因、基本特点、和数据库的区别

数据仓库的诞生原因 随着互联网的普及&#xff0c;信息技术已经深入到各行各业&#xff0c;并逐步融入到企业的日常运营中。然而&#xff0c;当前企业在信息化建设过程中遇到了一些困境与挑战。 1、历史数据积存。 过去企业的业务系统往往是在较长时间内建设的&#xff0c;很…

JavaSE编程题目练习(三)

博客昵称&#xff1a;架构师Cool 最喜欢的座右铭&#xff1a;一以贯之的努力&#xff0c;不得懈怠的人生。 作者简介&#xff1a;一名Coder&#xff0c;欢迎关注小弟&#xff01; 博主小留言&#xff1a;哈喽&#xff01;各位CSDN的uu们&#xff0c;我是你的小弟Cool&#xff0…

2023.10.20期中考核复现(misc)

杂项题就是2023陇剑杯的题目&#xff0c;可能还是不感兴趣吧&#xff0c;自从打完蓝帽杯之后除了web以外什么都没看 flow analysis 1 题目&#xff1a;What is the backdoor file name that comes with the server?( Including file suffix) 服务器附带的后门文件名是什么&am…

【前端】图片裁剪路径绘制及图片不规则裁剪

说明 项目中可能需要用户根据展示的图片&#xff0c;然后绘制需要裁剪的路径&#xff0c;再根据绘制的坐标进行裁剪&#xff0c;以下是前端的裁剪路径绘制的代码示例&#xff0c;后端可以根据当前的获取到的坐标进行裁剪&#xff0c;裁剪的坐标保存在coordinate数组中。 代码 …

【Maven教程】(八):使用 Nexus 创建私服 ~

Maven 使用 Nexus 创建私服 1️⃣ Nexus简介2️⃣ 安装 Nexus2.1 下载 Nexus2.2 Bundle 方式安装 Nexus2.3 WAR 方式安装 Nexus2.4 登录 Nexus 3️⃣ Nexus 的仓库与仓库组3.1 Nexus 内置的仓库3.2 Nexus 仓库分类的概念3.3 创建 Nexus 宿主仓库3.4 创建 Nexus 代理仓库3.5 创…

计算机毕业设计 基于SpringBoot笔记记录分享网站的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

openGauss学习笔记-105 openGauss 数据库管理-管理用户及权限-默认权限机制

文章目录 openGauss学习笔记-105 openGauss 数据库管理-管理用户及权限-默认权限机制 openGauss学习笔记-105 openGauss 数据库管理-管理用户及权限-默认权限机制 数据库对象创建后&#xff0c;进行对象创建的用户就是该对象的所有者。openGauss安装后的默认情况下&#xff0c…

C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例

分割数组的最大值 相关知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例&#xff1a;付视频课程 二分 过些天整理基础知识 题目 给定一个非负整数数组 nums 和一个整数 m &#xff0c;你需要将这个数组分成 m 个非空的连续子数组。 设计一个算法…

聊聊分布式架构09——分布式中的一致性协议

目录 01从集中式到分布式 系统特点 集中式特点 分布式特点 事务处理差异 02一致性协议与Paxos算法 2PC&#xff08;Two-Phase Commit&#xff09; 阶段一&#xff1a;提交事务请求 阶段二&#xff1a;执行事务提交 优缺点 3PC&#xff08;Three-Phase Commit&#x…