当前位置: 首页 > news >正文

从源码看无界 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?.

相关文章:

  • Restful风格接口开发
  • 制造业项目管理如何做才能更高效?制造企业如何选择适配的数字化项目管理系统工具?
  • 【软件测试】bug 篇
  • 足迹在后 脚步向前
  • 过拟合、归一化、正则化、鞍点
  • java中的List集合去重
  • springBoot接入文心一言
  • 计算机视觉与深度学习 | 基于Matlab的钢筋计数
  • spark的堆外内存,是在jvm内还是操作系统内存内?
  • 什么是ISO20000认证,认证ISO20000有什么好处?重要意义
  • Pytest 自动化测试框架详解
  • 【物联网-RS-485】
  • MySQL InnoDB存储引擎中的日志系统解析:binlog、redo log、undo log
  • 笔记:代码随想录算法训练营day67:Floyd 算法精讲、A * 算法精讲 (A star算法) 严重超时完结,不过,撒花
  • HTML5+CSS前端开发【保姆级教学】+超链接标签
  • 如何保证本地缓存和redis的一致性
  • GEO全域优化白皮书:盈达科技如何打造AI生态中的认知护城河
  • 林纳斯·托瓦兹:Linux系统之父 Git创始人
  • Python 类方法
  • C2000 ADC和DAC实验
  • 杭勇已任常州市政协党组成员,此前任常州市委常委、秘书长
  • 国务院关税税则委:调整对原产于美国的进口商品加征关税措施
  • 董军同法国国防部长举行会谈
  • 权益类基金发行回暖,这些老将挂帅新基,谁值得买?
  • 美国政府信用卡被设1美元限额,10美元采购花一两小时填表
  • 阶跃星辰CEO姜大昕:追求智能上限仍是最重要的事,多模态的“GPT-4时刻”尚未到来