微前端与常用的微前端解决方案+qiankun原理

2,985 阅读6分钟

微前端是什么

简单来说 微前端是一个由多个应用组成的应用 或者 由多个微应用组成的一个主应用

换个角度来说:将庞大的整体拆成可控的小块。

微前端出现的背景

微服务关系

随着业务的发展,系统复杂度的上升,如何拆分大型项目的技术栈,进一步提高开发效率,以及成为行业的探索方向。 然后系统还是越来越复杂。随着业务的不断扩张,需求的不断新增,项目又慢慢的臃肿起来。于是推出微服务。

** 微服务注重解耦,微前端讲究聚合。 **

单项目出现的问题

  1. 单项目打包项目的等待时间长
  2. 技术栈定型后不好升级和更换
  3. 多个团队的协作,过于聚合,共同维护
  4. 各个业务相互影响,相互污染
  5. 历史项目旧逻辑拖累*

如果我希望:

  1. 不同团队都用不同的技术栈怎么办
  2. 希望每个团队都可以独立开发,独立部署怎么办?
  3. 希望抛弃原有历史代码包袱,使用新技术栈开发新的业务逻辑

以上问题的期望造就了微前端的

微前端架构

满足几个特性:

  1. 技术栈无关

图片5.png 2. 几个微应用可以使用不同的框架

  • 微应用独立开发,独立部署 图片1.png
  1. 仓库独立,各自部署
    • 微应用之间互不影响,稳定性高

image.png 4. 独立运行

  • 每个微应用之间状态数据隔离,状态不共享

图片6.png

应用场景:

一般微前端还是应用在管理端,移动端应用比较少。

比如:公司内有多个业务部门,业务部门各自都有一个业务管理后台,而管理部门提出 1 个聚合功能需求,需要将各个部门的业务管理后台合并到一个总后台中,加载各个业务管理后台到总后台上。这时候有些管理后台是 vue2 有些是 vue3 有些是 react 有些是 angular,由于技术框架不同,迁移的人力成本和时间成本都会很高。

而如使用微前端架构,则可以利用微前端架构的特性去用较低成本去完成这个需求,将总后台作为主应用,每个业务管理后台作为一个微应用。

几种常见微前端方案

路由跳转分发

image.png

如图,在主应用中,通过 URL 跳转的方式进入到另一个微应用

即通过路由分发到不同的、独立前端应用上。

路由通常可以是应用框架自带的路由来实现,又或者通过服务器的反向代理实现。

iframe

可以创建一个全新的独立的宿主环境,可以友好的在主应用中展示微应用。

介绍一下iframe的通讯方式:

// parent
<body>
    <iframe id="react_iframe" src="http://localhost:3000/"> </iframe>
</body>
<script>
// 父窗口传信息给子窗口
    document.getElementById("react_iframe")
    .contentWindow.postMessage("react", "http://localhost:3000/");
// 父窗口监听子窗口的信息
    window.addEventListener("message", function (e) {
        console.log("form_child_iframe", e);
    });
</script>

// child src:http://localhost:8080/"
// 子窗口监听父窗口传递的信息
window.addEventListener("message", receivePerentMessage, false);
function receivePerentMessage(event) {
    console.log("form_perent_window", event);
}

// 子窗口给父窗口传递信息
window.parent.postMessage("child", "*");

缺点:

  1. url 不同步。浏览器刷新时 iframe 数据状态会丢失、导航功能无法使用。
  2. UI 不协调。弹出一个遮罩弹窗只会在 iframe 内出现,Dom 的呈现不能跳出 iframe 的范围,如果要在浏览器居中弹出,需要通过 iframe 通讯在主应用中弹出兼容。
  3. 主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
  4. storage 也需要从微应用中透传出给主应用。
  5. 加载速度慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。

基于缺点,使用 iframe 做微前端,需要考虑:

  1. 设计管理应用机制
    1. 需要考虑主应用与微应用的交互
      1. 窗口中的组件的加载、卸载
      2. 拷贝公用 UI 组件到主应用,比如:浏览器全屏居中弹窗
  2. 设计应用通讯机制
    1. 数据共享
    2. 响应式刷新
    3. 事件监听

微应用化

微应用化即在开发和运行时,应用都是以单一、微小应用的形式存在。

qiankun.js框架

qiankun:qiankun 是一个基于  single-spa  的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

主应用配置配置方式

微应用各框架配置方式

qiankun.js的流程图

图片7.png

qiankun.js的通讯方式

// 在主应用中
// 主应用传递props属性给微应用
import { loadMicroApp } from 'qiankun';

microApp = loadMicroApp({
    name: 'app1',
    entry: '//localhost:1234',
    container: this.containerRef.current,
    props: { brand: 'qiankun' }, // 这里可以传递props属性给微应用
});
// 微应用中就获取到props中携带的brand数据
export async function mount(props) {
  renderApp(props);
}

qiankun.js自带的状态管理工具

如果微应用和微应用之间需要进行共享数据状态那应该怎么做 image.png

// 在主应用中的入口文件中
import { initGlobalStates } from 'qiankun';

// 初始化 state
const state={}
const actions = initGlobalState(state);
actions.onGlobalStateChange((state, prev) => {
  // state: 变更后的状态; prev 变更前的状态
  console.log(state, prev);
});
// 微应用中从生命周期 mount 中获取的props中拿到通信方法
export function mount(props) {
    // 在当前应用监听全局状态,有变更触发 callback
    props.onGlobalStateChange((state, prev) => {
        // state: 变更后的状态; prev 变更前的状态
        console.log(state, prev);
    });
    // set状态API
  props.setGlobalState(state); 
}

qiankun.js原理

qiankun样式隔离

如果多个微应用同时挂载在一份document上,怎么实现的样式隔离 image.png

  1. shadowDOM 默认启用 strictStyleIsolation选项 子应用的根节点创建一个shadowRoot。最终整个应用的所有DOM将形成一棵shadow tree。我们知道,shadowDom的特点是,它内部所有节点的样式对树外面的节点无效,因此自然就实现了样式隔离。
  • 缺点:UI框架的弹窗,如antd-modal元素直接挂在document.body,脱离了shadowTree,依然会污染全局。
  1. 给不同的子应用加入不同的前缀:less-loader、postcss-loader 的prefix功能,为不同的子应用样式加入属性值experimentalStyleIsolation选项

qiankun全局JS隔离、沙箱模式

表现:主应用和子应用能使用到同一份window的属性(主应用)。 主应用通过fetch HTML方式载入子应用,所以用的window是同一个window,所以多个子应用会有全局污染问题。

ProxySandBox 多实例[不会污染全局window]

  1. 遍历当前window的属性和方法创建一份window(fakeWindow)
  2. 通过Proxy劫持代理这份fakeWindow
  • set:在fakeWindow上设值,并且记录变更
  • get:window.window 或 window.self 或window.top 都指向poxy(防止穿透出去),从fakeWindow中去取值。
  • 激活active和卸载inactive周期都不会做还原操作。 源码和注释: 图片8.png
  1. snapshotSanBox 不支持Poxy API的浏览器降级处理[会污染全局window]
  • 主要是通过active激活子应用时记录通过遍历存储window备份当前window状态记录。
  • 在卸载是通过快照编译还原window对象实。 源码和注释: 图片9.png