121.乾坤js隔离机制

程序员 2024-9-16 09:46:47 81 0 来自 中国
1.jpg 仍旧从loadApp函数入手
export async function loadApp<T extends ObjectType>(  app: LoadableApp<T>,  configuration: FrameworkConfiguration = {},  lifeCycles?: FrameworkLifeCycles<T>,): Promise<arcelConfigObjectGetter> {      const {        singular = false,        sandbox = true,        excludeAssetFilter,        globalContext = window,        ...importEntryOpts      } = configuration;    if (sandbox) {        sandboxContainer = createSandboxContainer(          appInstanceId,          // FIXME should use a strict sandbox logic while remount, see https://github.com/umijs/qiankun/issues/518          initialAppWrapperGetter,          scopedCSS,          useLooseSandbox,          excludeAssetFilter,          global,          speedySandbox,        );        // 用沙箱的署理对象作为接下来使用的全局对象        global = sandboxContainer.instance.proxy as typeof window;        mountSandbox = sandboxContainer.mount;        unmountSandbox = sandboxContainer.unmount;      }`}createSandboxContainer方法,创建沙箱容器,根据欣赏器情况判断和设置项决定使用哪种沙箱模式,支持proxy的欣赏器,如果没有明确设置使用单应用沙箱则使用ProxySandbox
export function createSandboxContainer(  appName: string,  elementGetter: () => HTMLElement | ShadowRoot,  scopedCSS: boolean,  useLooseSandbox?: boolean,  excludeAssetFilter?: (url: string) => boolean,  globalContext?: typeof window,  speedySandBox?: boolean,      ) {          let sandbox: SandBox;          //判断欣赏器是否支持proxy,如果支持使用proxy,不支持使用快照沙箱          if (window.Proxy) {              //LegacySandbox:支持单应用的沙箱,ProxySandbox:支持多应用的沙箱            sandbox = useLooseSandbox ? new LegacySandbox(appName, globalContext) : new ProxySandbox(appName, globalContext);          } else {             //快照沙箱            sandbox = new SnapshotSandbox(appName);          }                          return {              instance:sandbox,              async mount(){                  //启动沙箱                 sandbox.active();                   //...省略一些副作用处理处罚代码              },              async unmount(){              //关闭沙箱                sandbox.inactive();              },          }      }比及子应用挂载的时间实验mount方法,此时由于loadApp中使用的是沙箱作为署理对象的,因此实验的mount方法即createSandboxContainer的返回结果,在返回的mount函数中启动沙箱。
关于快照沙箱的实现原理可以参考这篇文章,大概直接参考杨艺韬大佬写的乾坤系列源码的文章。
总结来说就是通过active 和Inactive来控制沙箱的见效和失效控制沙箱的启动和关闭,本质还是对window上属性的操作,区别在于直接操作还是通过署理操作。
而单应用和多应用的区别ProxySandbox为支持多应用的署理沙箱 ,LegacySandbox仅仅允许页面同时运行一个微应用 。
关于署理沙箱,单应用和多应用的区别没有看太懂?同样都是使用proxy实现的署理对象也是window为什么proxySandbox就可以实现多应用的隔离呢?
我想应该是由于这段代码,通过rawObjectDefineProperty实现对globalContext的深拷贝,如许每次新创建一个ProxySandbox实例就会重新初始化一个fakeWindow window情况,以此来到达多应用相互隔离的结果。
//Object.defineProperty直接在一个对象上定义一个新属性,大概修改一个已经存在的属性const rawObjectDefineProperty = Object.defineProperty;function createFakeWindow(globalContext: Window) {  // map always has the fastest performance in has check scenario  // see https://jsperf.com/array-indexof-vs-set-has/23  const propertiesWithGetter = new Map<ropertyKey, boolean>();  const fakeWindow = {} as FakeWindow;  //过滤不可设置的属性,  Object.getOwnPropertyNames(globalContext)    .filter((p) => {      const descriptor = Object.getOwnPropertyDescriptor(globalContext, p);      return !descriptor?.configurable;    })    .forEach((p) => {      const descriptor = Object.getOwnPropertyDescriptor(globalContext, p);      if (descriptor) {        const hasGetter = Object.prototype.hasOwnProperty.call(descriptor, 'get');        if (          p === 'top' ||          p === 'parent' ||          p === 'self' ||          p === 'window' ||          (process.env.NODE_ENV === 'test' && (p === 'mockTop' || p === 'mockSafariTop'))        ) {          descriptor.configurable = true;          if (!hasGetter) {            descriptor.writable = true;          }        }        if (hasGetter) propertiesWithGetter.set(p, true);        rawObjectDefineProperty(fakeWindow, p, Object.freeze(descriptor));      }    });  return {    fakeWindow,    propertiesWithGetter,  };}而LegacySandbox是直接操作目标对象的,从这边设置属性的代码可以看出来。
export default class LegacySandbox implements SandBox {  private setWindowProp(prop: PropertyKey, value: any, toDelete?: boolean) {    //设置的值不存在,删除window上的属性    if (value === undefined && toDelete) {      // eslint-disable-next-line no-param-reassign      delete (this.globalContext as any)[prop];    } else if (isPropConfigurable(this.globalContext, prop) && typeof prop !== 'symbol') {      //window上存在的属性而且可操作,给属性设置值      Object.defineProperty(this.globalContext, prop, { writable: true, configurable: true });      // eslint-disable-next-line no-param-reassign      (this.globalContext as any)[prop] = value;    }  }}乾坤框架系列学习
01.学习微前端架构-乾坤
02.资源加载过程分析
03.乾坤css加载机制
04.乾坤js隔离机制
乾坤沙箱实现原理
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 18:26, Processed in 0.147936 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表