微前端——资源隔离

70 阅读2分钟

微前端

微前端, 类似于微服务概念,可以满足多个应用可以独立开发,测试,部署,然后可以整合到一个应用中,对外交互还是一个整体;

资源隔离

要实现微前端,应用在运行时加载,并且多个应用同时共存时,需要解决资源隔离。资源隔离分为,css资源和js资源

css资源隔离

shadow DOM

image.png

CSS scope

  • css隔离方案类似 vue scope
  • css.process, 遍历每个样式表, 为每个css规则添加上前缀

image.png image.png

js资源隔离

iframe

  • iframe具有天然隔离的能力,css隔离,js隔离,但是有以下缺点,在腾讯推出的wujie微前端架构中采用了这一方案,解决相关副作用。
  • 无界
    • 缺点:
      • 路由状态丢失,刷新一下,iframe的url状态就丢失了;
      • dom割裂严重,弹窗只能在iframe内部展示,无法覆盖全局
      • 每次打开白屏时间太长,对于spa应该不能结束;

沙箱隔离

  • 以qiankun为代表的两种js沙箱机制
    • sandbox沙箱——只适合单一的子应用,
      • 激活将window上的属性遍历,暂存到windowSnapshot上,激活时修改的属性也存放到window, 删除的属性删除;
      • 未激活,diff,将修改属性和删除属性暂存起来;
/**
 * @author Hydrogen
 * @since 2020-3-8
 */
import type { SandBox } from '../interfaces';
import { SandBoxType } from '../interfaces';

function iter(obj: typeof window | Record<any, any>, callbackFn: (prop: any) => void) {
  for (const prop in obj) {
    if (obj.hasOwnProperty(prop) || prop === 'clearInterval') {
      callbackFn(prop);
    }
  }
}

/**
 * 基于 diff 方式实现的沙箱,用于不支持 Proxy 的低版本浏览器
 */
export default class SnapshotSandbox implements SandBox {
  proxy: WindowProxy;
  name: string;
  type: SandBoxType;
  sandboxRunning = true;
  private windowSnapshot!: Window;
  private modifyPropsMap: Record<any, any> = {};
  private deletePropsSet: Set<any> = new Set();
  constructor(name: string) {
    this.name = name;
    this.proxy = window;
    this.type = SandBoxType.Snapshot;
  }

  active() {
    // 记录当前快照
    this.windowSnapshot = {} as Window;
    iter(window, (prop) => {
      this.windowSnapshot[prop] = window[prop];
    });

    // 恢复之前的变更
    Object.keys(this.modifyPropsMap).forEach((p: any) => {
      window[p] = this.modifyPropsMap[p];
    });
    // 删除之前删除的属性
    this.deletePropsSet.forEach((p: any) => {
      delete window[p];
    });
    this.sandboxRunning = true;
  }

  inactive() {
    this.modifyPropsMap = {};
    this.deletePropsSet.clear();
    iter(window, (prop) => {
      if (window[prop] !== this.windowSnapshot[prop]) {
        // 记录变更,恢复环境
        this.modifyPropsMap[prop] = window[prop];
        window[prop] = this.windowSnapshot[prop];
      }
    });
    iter(this.windowSnapshot, (prop) => {
      if (!window.hasOwnProperty(prop)) {
        // 记录被删除的属性,恢复环境
        this.deletePropsSet.add(prop);
        window[prop] = this.windowSnapshot[prop];
      }
    });
    this.sandboxRunning = false;
  }

  patchDocument(): void {}
    }

  • proxySandbox沙箱
    • proxy了createFakeWindow的set, get, has, definedperproty等方法;
    • 而FakeWindow是通过复制了window上的document、location、top、window拷贝了一份;
    • 真正做到了不污染window, 并且支持多实例;