qiankun学习记录

什么是微前端

微前端是指存在于浏览器中的微服务,其借鉴了微服务的架构理念,将微服务的概念扩展到了前端。

如果对微服务的概念比较陌生的话,可以简单的理解为微前端就是将一个大型的前端应用拆分成多个模块,每个微前端模块可以由不同的团队进行管理,并可以自主选择框架,并且有自己的仓库,可以独立部署上线。

微前端的好处

1.团队自治
2.兼容老项目
3.跨技术栈

现有的微前端方案

1.iframe
通过iframe标签来嵌入到父应用中,iframe具有天然的隔离属性,各个子应用之间以及子应用和父应用之间都可以做到互不影响。

iframe的缺点:

  1. url不同步,如果刷新页面,iframe中的页面的路由会丢失。
  2. 全局上下文完全隔离,内存变量不共享。
  3. UI不同步,比如iframe中的页面如果有带遮罩层的弹窗组件,则遮罩就不能覆盖整个浏览器,只能在iframe中生效。
  4. 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
2.single-spa
官网:https://zh-hans.single-spa.js.org/docs/getting-started-overviewsingle-spa是最早的微前端框架,可以兼容很多技术栈。

single-spa的缺点:

  1. 没有实现js隔离和css隔离
  2. 需要修改大量的配置,包括基座和子应用的,不能开箱即用
3.qiankun
qiankun是阿里开源的一个微前端的框架qiankun的优点:
- 基于single-spa封装的,提供了更加开箱即用的API
- 技术栈无关,任意技术栈的应用均可使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。
- HTML Entry的方式接入,像使用iframe一样简单
- 实现了single-spa不具备的样式隔离和js隔离
- 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
  • 基座(主应用):主要负责集成所有的子应用,提供一个入口能够访问你所需要的子应用的展示,尽量不写复杂的业务逻辑
  • 子应用:根据不同业务划分的模块,每个子应用都打包成umd模块的形式供基座(主应用)来加载
基座改造

基座用的是create-react-app脚手架加上antd组件库搭建的项目,也可以选择vue或者其他框架,一般来说,基座只提供加载子应用的容器,尽量不写复杂的业务逻辑。

  1. 安装qiankun
// 安装qiankun
npm i qiankun // 或者 yarn add qiankun
  1. 修改入口文件
// 在src/index.tsx中增加如下代码
import { start, registerMicroApps} from 'qiankun'
// 1.要加载的子应用列表
const apps = [{name: 'sub-react', // 子应用的名称entry: '//localhost:3001', // 默认会加载这个路径下的html,解析里面的jsactiveRule: '/sub-react',// 匹配的路由container: '#sub-app' // 子应用加载的容器},
] // 2. 注册子应用
registerMicroApps(apps, {beforeLoad: [async app => console.log('before load', app.name)],beforeMount: [async app => console.log('before mount', app.name)],afterMount: [async app => console.log('after mount', app.name)],afterUnmount: [async app => console.log('after unmount', app.name)]
})// 3. 启动微服务
start()
react子应用

使用create-react-app脚手架创建,webpack进行配置,为了不eject所有的webpack配置,我们选择用react-app-rewired工具来改造webpack配置。
2. 修改入口文件

// 在src/index.tsx中增加如下代码
import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom'
import './public-path'let root: any;
// 将render方法用函数包裹,供后续主应用与独立运行调用
function render(props: any) {const { container } = props// 1.拿到root的divconst dom = container ? container.querySelector('#root') : document.getElementById('root')root = createRoot(dom)// 2.把app渲染到root上面// basename对应的是基座里面子应用列表的路由// 因为基座加载子应用的时候是匹配路由的root.render(<BrowserRouter basename='/sub-react'><App/></BrowserRouter>)
}// 判断是否在qiankun环境下,非qiankun环境下独立运行
if(!(window as any).__POWERED_BY_QIANKUN__) {render({})
}// 各个生命周期
// bootstrap 置灰在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用mount钩子,不会再重复触发 bootstrap
export async function bootstrap() {console.log('react app bootstrap');
}// 应用每次进入都会调用mount方法,通常我们在这里触发应用的渲染方法
export async function mount(props: any) {console.log('props==', props);render(props)
} // 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
export async function unmount(props: any) {root.unmount()
}
  1. 新增public-path.js
if (window.__POWERED_BY_QIANKUN__) {// 动态设置 webpack publicPath,防止资源加载出错// eslint-disable-next-line no-undef__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
  1. 修改webpack配置文件
// 在根目录下新增config-overrides.js文件并新增如下配置
const { name } = require("./package");module.exports = {webpack: (config) => {config.output.library = `${name}-[name]`;// 把项目打包成umd模块,方便qiankun去读取我们暴露出来的生命周期(bootstrap、mount、unmount)config.output.libraryTarget = "umd";config.output.chunkLoadingGlobal = `webpackJsonp_${name}`;return config;}
};
vue子应用
# 创建子应用,选择vue3+vite
npm create vite@latest
改造子应用
  1. 安装vite-plugin-qiankun依赖包
npm i vite-plugin-qiankun # yarn add vite-plugin-qiankun
  1. 修改vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun';export default defineConfig({base: '/sub-vue', // 和基座中配置的activeRule一致server: {port: 3002, // 端口cors: true, // 允许跨域origin: 'http://localhost:3002'  // 指定允许跨域请求的来源地址},plugins: [vue(),// 加一个qiankun,写子应用的名称,需要开发模式的需要配置useDevModeqiankun('sub-vue', { // 配置qiankun插件// 是否运行在开发模式下useDevMode: true})]
})
  1. 修改main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'let app: any;
// 判断是不是在qiankun环境下
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {createApp(App).use(router).mount('#app');
} else {renderWithQiankun({// 子应用挂载(来回切换的时候)mount (props){ // 挂载的时候app = createApp(App);app.use(router).mount(props.container.querySelector('#app'))},// 只有子应用第一次加载会触发bootstrap () { // 应用刚加载的时候console.log('vue app bootstrap');},// 更新update () { // 更新console.log('vue app update');},// 卸载unmount () { // 卸载console.log('vue app unmount');app?.unmount();}})
}
umi子应用

使用umi4去创建子应用,创建好后只需要简单的配置就可以跑起来

  1. 安装插件
npm i @umijs/plugins
  1. 配置.umirc.ts
export default {base: '/sub-umi',npmClient: 'npm',plugins: ['@umijs/plugins/dist/qiankun'],qiankun: {slave: {},}
};
// 在入口文件导出qiankun的生命周期
export const qiankun = {async mount(props:any) {console.log(props);},async bootstrap () {console.log('umi app bootstraped');},async afterMount(props: any) {console.log('umi app afterMount', props);},
}

补充

1.样式隔离

qiankun内部实现的是子应用之间的样式隔离,基座和子应用之间的样式么没有进行隔离

应用间的样式隔离原因:子应用之间的样式隔离很简单,加载子应用的样式,卸载的时候把子应用的样式进行卸载,加载下一个子应用的时候加载下一个子应用的样式

 1.样式隔离1.1 每个应用的样式使用固定的格式1.2 通过css-module的方式给每个应用自动添加上前缀修改基座应用公共样式的时候还是会影响子应用的样式,这时候可以把子应用的样式优先级提高一点 (样式隔离)
2.子应用间的跳转

2.1 主应用和微应用都是hash模式,主应用根据hash来判断微应用,则不用考虑这个问题

通过location.hash直接修改hash值
http://localhost:3001/#/react-app/list
修改为
http://localhost:3001/#/vue-app/list

2.2 history模式下应用之间的跳转或者微应用跳主应用页面,直接使用微应用的路由实例是不行的,原因是微应用的路由实例跳转都基于路由的base。

2.2.1 history.pushState()
2.2.2 将主应用的路由实例通过props传给微应用,微应用这个路由实例跳转
// 基座和子应用用的都是一个windows对象,可以在基座中复写并监听history.pushState()方法并做相应的跳转逻辑// 在app.tsx重写pushState
// 重写函数// 重写函数const _wr = function (type: string) {// 拿到windos 的history对象传过来的参数const org = (window as any).history[type]return function() {// 拿到org后对他重新调用下const rv = org.apply(this, arguments);const e: any = new Event(type)// 返回发布一个自定义事件e.arguments = argumentswindow.dispatchEvent(e)return rv}}// 把重写的函数赋值给window上pushStatewindow.history.pushState = _wr('pushState')// 在这个函数中做跳转后的逻辑const bindHistory = () => {const currentPath = window.location.pathname;setSelectedPath(routes.find(item => currentPath.includes(item.key))?.key || '')}// 绑定事件window.addEventListener('pushState', bindHistory)
公共依赖加载

3.1 场景:如果主应用和子应用都使用了相同的库或者包(antd, axios等),就可以用externals(外部扩展)的方式来引入,减少加载重复报导致资源浪费,就是一个 项目使用后另一个项目不必再重复加载。

3.2.1 主应用:将所有公共依赖配置webpack的externals,并且在index.html使用外链引入这些公共依赖
3.2.2 子应用:和主应用一样配置webpack的externals,并且在index.html使用外链引入这些公共依赖,注意,还需要给子应用的公共依赖加上ignore属性(这是自定义的属性, 非标准属性),
qiankun在解析时如果发现ignore属性就会自动忽略

以axios为例:
基座的配置

// 修改config-overrides.js
const { override, addWebpackExternals } = require('customize-cra')// 这个配置就是通过外链的方式去引入这个包
module.exports = override(addWebpackExternals({axios: "axios"})
)// 在publi目录下的index.html添加外链
<!-- 注意:这里的公共依赖的版本必须和子应用一致 -->
<script src="https://unpkg.com/axios@1.1.2/dist/axios.min.js"></script>

子应用的配置

// 在umi-app子应用中
// 修改.umirc.ts配置文件
export default {base: '/sub-umi',npmClient: 'npm',plugins: ['@umijs/plugins/dist/qiankun'],qiankun: {slave: {},},headScripts: [{ // 配置外链地址,和设置忽略,qiankun在解析时如果发现ignore属性就会自动忽略src: 'https://unpkg.com/axios@1.1.2/dist/axios.min.js', ignore: true}]
};
全局状态管理

一般来说,各个子应用是通过业务来划分的,不同业务线应该降低耦合度,尽量去避免通信,但是如果涉及到一些公共的状态或者操作,qiankun也是支持的。

qiankun提供了一个全局的GLobalState来共享数据,基座初始化之后,子应用可以监听到这个数据的变化,也能提交这个数据

// 基座的配置
// 在src/index.tsx中增加如下代码
import {  initGlobalState } from 'qiankun'
// 基座初始化
const state = { count: 1 }
const actions = initGlobalState(state);
// 基座项目监听和修改
actions.onGlobalStateChange((state, prev) => {// state: 变更后的状态; prev 变更前的状态console.log(state, prev);
})
actions.setGlobalState(state)
// 子应用的配置
// 在src/index.tsx中增加如下代码
// 在子应用的mount生命周期监听
export async function mount(props: any) {console.log('props==', props);// 子项目监听和修改// 然后在子应用中拿到这两个函数,然后给他设置一个count:2props.onGlobalStateChange((state,prev) => {// state: 变更后的状态, prev 变更前的状态console.log('子应用state===',state,prev)// 一般是将这个state存储到我们子应用的store,然后在其他组件中去用// 这样就是实现了一个简单基座和子应用之间的通信// 同样在其他子应用中想要用到基座传过来的状态也是这样用的,// 修改的话也是调用这个setGlobalState// 监听变化也是调用onGlobalStateChange})props.setGlobalState({ count: 2 })render(props)
} 

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

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

相关文章

配置中心 选型 : Apollo Vs. Nacos Vs. spring cloud config

为什么我们需要一个微服务配置中心&#xff1f; 首先&#xff0c;我们可以想象下&#xff0c;如果没有配置中心&#xff0c;我们的项目可能是这样的&#xff1a;不同环境的配置文件都放在项目里面&#xff0c;部署时可以通过启动参数来指定使用哪个环境的配置。 这种方式有两…

HarmonyOS(65) ArkUI FrameNode详解

Node 1、Node简介2、FrameNode2.1、创建和删除节点2.2、对FrameNode的增删改2.3、 FramNode的查询功能3、demo源码4、总结5、参考资料1、Node简介 在HarmonyOS(63) ArkUI 自定义占位组件NodeContainer介绍了自定义节点复用的原理(阅读本本篇博文之前,建议先读读这个),在No…

详解RabbitMQ在Ubuntu上的安装

​​​​​​​ 目录 Ubuntu 环境安装 安装Erlang 查看Erlang版本 退出命令 ​编辑安装RabbitMQ 确认安装结果 安装RabbitMQ管理界面 启动服务 查看服务状态 通过IP:port访问 添加管理员用户 给用户添加权限 再次访问 Ubuntu 环境安装 安装Erlang RabbitMq需要…

vue图片之放大、缩小、1:1、刷新、左切换、全屏、右切换、左旋咋、右旋转、x轴翻转、y轴翻转

先上效果&#xff0c;代码在下面 <template><!-- 图片列表 --><div class"image-list"><img:src"imageSrc"v-for"(imageSrc, index) in images":key"index"click"openImage(index)"error"handleI…

【计算机网络】实验12:网际控制报文协议ICMP的应用

实验12 网际控制报文协议ICMP的应用 一、实验目的 验证ping命令和tracert命令的工作原理。 二、实验环境 Cisco Packet Tracer模拟器 三、实验过程 1.构建网络拓扑并进行信息标注&#xff0c;将所需要配置的IP地址写在对应的主机或者路由器旁边&#xff0c;如图1所示。 图…

迭代器模式的理解和实践

引言 在软件开发中&#xff0c;我们经常需要遍历容器对象&#xff08;如数组、列表、集合等&#xff09;中的元素。如果每个容器对象都实现自己的遍历算法&#xff0c;那么代码将会变得冗余且难以维护。为了解决这个问题&#xff0c;迭代器模式应运而生。迭代器模式是一种行为型…

TS2339: Property ‘value‘ does not exist on type ‘MessageBoxData‘.

1、源代码 <template><el-dialog:visible"visible":before-close"handleClose":close-on-click-modal"false"title"邀请码"width"1200px"append-to-bodydestroy-on-close><div class"invite-code-wrap…

ubuntu防火墙(三)——firewalld使用与讲解

本文是Linux下&#xff0c;用ufw实现端口关闭、流量控制(二) firewalld使用方式 firewalld 是一个动态管理防火墙的工具&#xff0c;主要用于 Linux 系统&#xff08;包括 Ubuntu 和 CentOS 等&#xff09;。它提供了一个基于区域&#xff08;zones&#xff09;和服务&#x…

Windows 安装配置 RabbitMQ 详解

博主介绍&#xff1a; 计算机科班人&#xff0c;全栈工程师&#xff0c;掌握C、C#、Java、Python、Android等主流编程语言&#xff0c;同时也熟练掌握mysql、oracle、sqlserver等主流数据库&#xff0c;能够为大家提供全方位的技术支持和交流。 工作五年&#xff0c;具有丰富的…

R语言的数据结构--矩阵

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言医学数据分析实践-R语言的数据结构-CSDN博客 矩阵是一个二维数组&#xff0c;矩阵中的元素都具有相…

JAVA基础学习笔记_反射+动态代理

文章目录 反射获取class对象的三种方式获取构造方法获取成员变量获取成员方法反射的作用 动态代理 反射 允许对成员变量\成员方法\构造方法的信息进行编程访问 把类内的信息扒的干干净净,获取解剖 获取从class字节码文件中获取 获取class对象的三种方式 public static void …

微信小程序一键复制功能

wx.setClipboardData(Object object) 设置系统剪贴板的内容。调用成功后&#xff0c;会弹出 toast 提示"内容已复制"&#xff0c;持续 1.5s wx.setClipboardData({data: 你需要复制的内容,success (res) {wx.getClipboardData({success (res) {console.log(res.dat…

【Python网络爬虫 常见问题汇总】

目录 1. 爬取图片出现403解决办法&#xff1a;设置请求头中的Referer字段 2.关于干坏事的问题后续不定期更新 欢迎共同探讨学习进步 1. 爬取图片出现403 问题出自案例9&#xff0c;已解决。 【Python网络爬虫笔记】9- 抓取优美图库高清壁纸 当在爬取图库图片时遇到 403 错误…

Linux: docker: 怎么修改 proc下的文件内容?

文章目录 参考问题方法 1:在宿主机上修改参数方法 2:启动容器时挂载 /proc 为可写方法 3:通过 Kubernetes 调整配置方法 4:构建特权容器参考 https://docs.docker.com/security/for-admins/hardened-desktop/enhanced-container-isolation/features-benefits/#procfs–sys…

分布式 分布式事务 总结

前言 相关系列 《分布式 & 目录》《分布式 & 分布式事务 & 总结》《分布式 & 分布式事务 & 问题》 分布式事务 所谓分布式事务是指操作范围笼罩多个不同节点的事务。例如对于订单节点&库存节点而言&#xff0c;一次完整的交易需要同时调动两个节…

STM32+模拟或硬件IIC+SHT20驱动问题:接上拉电阻、BUSY死锁?

主要问题&#xff1a; 1&#xff0c;使用STM32F103C8T6&#xff0c;模拟IIC&#xff0c;SCL和SDA口配置为推挽输出上拉&#xff0c;主要是SDA脚&#xff0c;每次都要输出输入模式重新配置&#xff0c;虽然也能通信&#xff0c;但不稳定&#xff0c;出错率大&#xff1b; 2&…

【工业机器视觉】基于深度学习的水表盘读数识别(3-数据标注与转换)

【工业机器视觉】基于深度学习的仪表盘识读&#xff08;2&#xff09;-CSDN博客 数据标注 标注扩展 Labelme 和 LabelImg 都是用于创建机器学习和计算机视觉项目所需标注数据的工具。它们都允许用户通过图形界面手动标注图像&#xff0c;但各自有其特点和适用场景。 Labelme…

静态路由与交换机配置实验

1.建立网络拓扑 添加2台计算机&#xff0c;标签名为PC0、PC1&#xff1b;添加2台二层交换机2960&#xff0c;标签名为S0、S1&#xff1b;添加2台路由器2811&#xff0c;标签名为R0、R1&#xff1b;交换机划分的VLAN及端口根据如下拓扑图&#xff0c;使用直通线、DCE串口线连接…

【Spark】Spark Join类型及Join实现方式

Spark Join类型 1. Inner Join (内连接) 示例&#xff1a;val result df1.join(df2, df1("id") df2("id"), "inner")执行逻辑&#xff1a;只返回那些在两个表中都有匹配的行。 2. Left Join (左外连接) 示例&#xff1a;val result df1.jo…

socket UDP 环路回显的服务端

基于socket通讯的方式&#xff0c;无论用http或者udp或者自定义的协议&#xff0c;程序结构都是类似的。这个以UDP协议为例简要说明。 #include <stdio.h> // 标准输入输出库 #include <sys/types.h> // 提供了一些数据类型&#xff0c;如ssize_t #include <sy…