从源码看无界 1.0.28:为何说它是 qiankun 的 “轻量化替代方案”(二)

我们接着上一节的《从源码看无界 1.0.28:为何说它是 qiankun 的 “轻量化替代方案”》内容继续往下。

生命周期图

在这里插入图片描述

sandbox.active 方法

我们找到 packages/wujie-core/src/sandbox.ts 文件的第 275 行:

//...

/** 激活子应用
 * 1、同步路由
 * 2、动态修改iframe的fetch
 * 3、准备shadow
 * 4、准备子应用注入
 */
public async active(options: {
  url: string;
  sync?: boolean;
  prefix?: { [key: string]: string };
  template?: string;
  el?: string | HTMLElement;
  props?: { [key: string]: any };
  alive?: boolean;
  fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
  replace?: (code: string) => string;
}): Promise<void> {
  //...
  // 设置loading
    addLoading(el, loading);
    const newSandbox = new WuJie({ name, url, attrs, degradeAttrs, fiber, degrade, plugins, lifecycles });
    newSandbox.lifecycles?.beforeLoad?.(newSandbox.iframe.contentWindow);
    const { template, getExternalScripts, getExternalStyleSheets } = await importHTML({
      url,
      html,
      opts: {
        fetch: fetch || window.fetch,
        plugins: newSandbox.plugins,
        loadError: newSandbox.lifecycles.loadError,
        fiber,
      },
    });
  const processedHtml = await processCssLoader(newSandbox, template, getExternalStyleSheets);
    await newSandbox.active({ url, sync, prefix, template: processedHtml, el, props, alive, fetch, replace });
    await newSandbox.start(getExternalScripts);
    return () => newSandbox.destroy();
}
//...

主子应用路由同步

可以看到,首先将主应用的路由同步到子应用中,然后再把子应用的路由同步到主应用中:

// 处理子应用路由同步
  if (this.execFlag && this.alive) {
    // 当保活模式下子应用重新激活时,只需要将子应用路径同步回主应用
    syncUrlToWindow(iframeWindow);
  } else {
    // 先将url同步回iframe,然后再同步回浏览器url
    syncUrlToIframe(iframeWindow);
    syncUrlToWindow(iframeWindow);
  }
  
  //...
  /**
 * 同步主应用路由到子应用
 */
export function syncUrlToIframe(iframeWindow: Window): void {
 // ...
  if (preAppRoutePath !== appRoutePath) {
    iframeWindow.history.replaceState(null, "", inject.mainHostPath + appRoutePath);
  }
}

/**
 * 同步子应用路由到主应用路由
 */
export function syncUrlToWindow(iframeWindow: Window): void {
  //...
  if (winUrlElement.href !== window.location.href) {
    window.history.replaceState(null, "", winUrlElement.href);
  }
  winUrlElement = null;
}

可以看到同步路由也是很简单,直接调用 history 的 replaceState 方法把主应用跟子应用的 history 改掉了。

接来下可以看到 active 方法中的这段代码:

我们接着上一节的《从源码看无界 1.0.28:为何说它是 qiankun 的 “轻量化替代方案”》内容继续往下。
生命周期图
sandbox.active 方法
我们找到 packages/wujie-core/src/sandbox.ts 文件的第 139 行:
//...

/** 激活子应用
 * 1、同步路由
 * 2、动态修改iframe的fetch
 * 3、准备shadow
 * 4、准备子应用注入
 */
public async active(options: {
   
  url: string;
  sync?: boolean;
  prefix?: {
    [key: string]: string };
  template?: string;
  el?: string | HTMLElement;
  props?: {
    [key: string]: any };
  alive?: boolean;
  fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
  replace?: (code: string) => string;
}): Promise<void> {
   
 //...

  if (this.shadowRoot) {
   
    /*
     document.addEventListener was transfer to shadowRoot.addEventListener
     react 16 SyntheticEvent will remember document event for avoid repeat listen
     shadowRoot have to dispatchEvent for react 16 so can't be destroyed
     this may lead memory leak risk
     */
    this.el = renderElementToContainer(this.shadowRoot.host, el);
    if (this.alive) return;
  } else {
   
    // 预执行无容器,暂时插入iframe内部触发Web Component的connect
    const iframeBody = rawDocumentQuerySelector.call(iframeWindow.document, "body") as HTMLElement;
    this.el = renderElementToContainer(createWujieWebComponent(this.id), el ?? iframeBody);
  }

  await renderTemplateToShadowRoot(this.shadowRoot, iframeWindow, this.template);
  this.patchCssRules();

  // inject shadowRoot to app
  this.provide.shadowRoot = this.shadowRoot;
}

我们第一次渲染子应用的时候,这个 shadowRoot 对象肯定是没有初始化的,所以会走以下代码:

// 预执行无容器,暂时插入iframe内部触发Web Component的connect
    const iframeBody = rawDocumentQuerySelector.call(iframeWindow.document, "body") as HTMLElement;
    this.el = renderElementToContainer(createWujieWebComponent(this.id), el ?? iframeBody);
    
    //...
    export function createWujieWebComponent(id: string): HTMLElement {
      const contentElement = window.document.createElement("wujie-app");
      contentElement.setAttribute(WUJIE_APP_ID, id);
      contentElement.classList.add(WUJIE_IFRAME_CLASS);
      return contentElement;
    }}

创建自定义元素 wujie-app

可以看到,首先调用了 createWujieWebComponent 方法注册了一个自定义元素 wujie-app,那么有小伙伴要问了,这个 “wujie-app” 元素到底是啥?

其实在框架加载的时候就已经往全局注册了一个自定义元素 wujie-app,可以找到 packages/wujie-core/src/index.ts 文件的第 171 行代码:

/**
 * 定义 wujie webComponent,将shadow包裹并获得dom装载和卸载的生命周期
 */
export function defineWujieWebComponent() {
   
  const customElements = window.customElements;
  if (customElements && !customElements?.get("wujie-app")) {
   
    class WujieApp extends HTMLElement {
   
      connectedCallback(): void {
   
        if (this.shadowRoot) return;
        // 为自定义元素附加一个 Shadow DOM,实现样式和结构的封装
        const shadowRoot = this.attachShadow({
    mode: "open" });
        const sandbox = getWujieById(this.getAttribute(WUJIE_APP_ID));
        patchElementEffect(shadowRoot, sandbox.iframe.contentWindow);
        sandbox.shadowRoot = shadowRoot;
      }

      disconnectedCallback(): void {
   
        const sandbox = getWujieById(this.getAttribute(WUJIE_APP_ID));
        sandbox?.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值