微前端---qiankun

115 阅读5分钟

前提 blog.csdn.net/yezi__6/art…

微前端方案有:京东的micro-app,腾讯的wujie,阿里的qian kun等 qiankun相较于iframe具有一些优势:

  • 更好的路由前进后退
  • 更自然的弹窗遮罩层覆盖可视范围
  • 更方便的页面间消息传递

微前端的定义

微前端是一种前端架构方法,将一个庞大的前端应用拆分为多个独立灵活的小型应用,每个应用都可以独立开发、独立运行、独立部署,再将这些小型应用联合为一个完整的应用。

微前端的实现

当路由切换时,下载对应应用的代码,在容器里运行。 核心原理是利用了 JavaScript 的 Proxy 对象实现沙箱环境。这种沙箱环境可以实现对子应用的隔离,使其不影响主应用或其他子应用。

微前端框架两大共性问题

  • 应用的加载与切换。包括路由的处理、应用加载的处理和应用入口的选择。
  • 应用的隔离与通信。JS的隔离、样式的隔离、父子之间的通信问题。

qiankun

qiankun注册微应用的方式

手动模式:使用loadMicroApp手动注册微应用

自动模式:使用registerMicroApps+start,路由变化加载应用

应用之间的通信

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

qiankun JS沙箱

  • [快照沙箱] SnapshotSandbox:记录 window 对象,每次 unmount 都要和微应用的环境进行 Diff

    • 原理: 沙箱激活时,会讲window的快照信息存在windowSnapshot中,如果modifyPropsMap有值,还需要还原上次的状态;激活期间,可能修改了w indow的数据,退出沙箱时,将修改过的信息存在modifyPropsMap里,并且把window还原成初始进入的状态
    • 原理(代码版):通过new snapshotSanbox(),激活沙箱,将window的快照信息存到windowSnapshot里面,并且从modifyPropsMap还原上次修改的值,退出沙箱,对windowSnapshot和当前window做diff,将变更的值存到modifypropsMap,同时将windowSnapshot还原到window。
  • [单例沙箱] LegacySandbox:在微应用修改 window.xxx 时直接记录 Diff,将其用于环境恢复

  • [代理沙箱] ProxySandbox:为每个微应用分配一个 fakeWindow,当微应用操作 window 时,其实是在 fakeWindow 上操作。

    • 激活沙箱后,每次对window取值时,先从自己沙箱环境的fakeWindow里面找,如果不存在,就从rawWindow(外部的window)里找;当对沙箱内部的window对象赋值时,会直接操作fakeWindow,而不会影响到rawWindow.

要和这些沙箱结合起来使用,qiankun 会把要执行的 JS 包裹在立即执行函数中,通过绑定上下文和传参的方式来改变 this 和 window 的值,让它们指向 window.proxy 沙箱对象,最后再用 eval 来执行这个函数。

简化版qiankun JS沙箱实现原理代码

// 1. 使用 Proxy 创建一个空对象,该对象是子应用运行的沙箱环境
function createSandbox() {
  // 原始的 window 对象
  const rawWindow = window;

  // 2. 创建一个代理,在这个代理上捕获对 window 的读/写操作
  const sandbox = new Proxy({}, {
    // 当读取属性值时触发
    get(target, key) {
      // 3. 如果子应用读取 window 的属性,实际上是读取原始 window 上的属性
      return rawWindow[key];
    },
    // 当设置属性值时触发
    set(target, key, value) {
      // 4. 当子应用试图修改 window 属性时,实际上修改的是这个代理对象,而不是原始的 window
      // 这样就实现了对子应用对 window 修改的隔离
      target[key] = value;
      return true;
    }
  });

  return sandbox;
}

// 使用
const sandbox = createSandbox();

// 子应用代码运行在沙箱中,比如:
sandbox.someProperty = 'Hello from child app';
console.log(window.someProperty);  // undefined
console.log(sandbox.someProperty);  // 'Hello from child app'

// 注意:这只是 qiankun 沙箱实现的一个简化版本,真实的 qiankun 沙箱实现会考虑更多的细节和兼容性问题。

qiankun CSS沙箱

背景

主应用中引入多个子应用时,子应用的样式会相互影响。(因为子应用的样式是全局的)。

  • 子应用样式不会影响到其他子应用或主应用,同时支持全局样式
  • 主应用样式可以覆盖各子应用下的指定的样式,不容易影响到其他子应用

qiankun提供的样式隔离方案

  • Qiankun 的样式隔离主要分为 strictStyleIsolation 以及 experimentalStyleIsolation 两种
strictStyleIsolation(主要是通过Shadow DOM 实现)
  • Shadow DOM 子应用的根节点创建一个shadow root,最终整个应用的所有DOM形成一颗shadow tree。(特点:内部所有节点的样式对树外面的节点无效,实现样式隔离)
    • 取出当前元素内容
    • 生成shadowDOM
    • 取出内容放到shadowDOM
    • 清除这个元素,追加到shadow DOM上
特例:当启用strictStylelsolation时,Element-plus组件的部分样式会失效?

原因:Element-plus的全局变量是通过:root选择器作用到根节点的,但是在shadow dom中,无法通过:root选中根节点,导致部分样式失效。【juejin.cn/post/726231…

解决思路:自定义loader,在webpack打包文件时把:root替换成:host

const myStyleLoader = function(source){
  return source.replace(/:root/g,':host')
}
module.exports = myStyleLoader

修改webpack配置规则

modules:{
 rules:[
  {
  test:/\.scss$/,
  include:[path.resolve(_dirname,'node_modules/element-plus/theme-chalk')]
   use:['style-loader','css-loader','my-style-loader','sass-loader']
  }
 ]
}
experimentalStyleIsolation(主要是通过scoped css 实现)[在子应用下面的样式会包一个特殊的选择器规则限定影响范围]
  • Scoped CSS 则是对 style 元素的 CSS 文本进行处理,在原有选择器上添加下个父类选择器,以此做样式隔离
    • 原理:将微应用的的文本提取出来,将所有的选择器进行替换,样式就只会作用在div[data-app-name=子应用名称]元素上

坑:动态类的样式会丢失?