qiankun 运行过程解析

1,387 阅读2分钟

问题来源

项目接入方式 上图是现有项目使用qiankun作为微前端的方式, 因为子应用路由间存在互相切换的情况,在主应用切换时会发生空屏。

问题定位

  1. 原本子应用可以正常切换,排除了react-route切换存在问题。
  2. 定位到qiankun, 通过主应用切换加载子应用正常,证明资源能正常运行。
  3. 问题应该就在页面切换后组件进行loadMicroApp时发生的。

运行解析

qiankun关键函数调用.png

通过源码debug,我们可以发现qiankun做的事情其实就是为single-spa去凑config参数。回到我们的问题,组件重新渲染时白屏。

  1. 通过debug发现memorizedLoadingFn会结合container(就是挂载点)去做事情。
  const memorizedLoadingFn = async (): Promise<ParcelConfigObject> => {
    ....
    const container = 'container' in app ? app.container : undefined;

    if (container) {
      // 查找到存在挂载点则直接用上次loadapp函数产生的生成配置进行渲染。
      const xpath = getContainerXpath(container);
      if (xpath) {
        const parcelConfigGetterPromise = appConfigPromiseGetterMap.get(`${name}-${xpath}`);
        if (parcelConfigGetterPromise) return wrapParcelConfigForRemount((await parcelConfigGetterPromise)(container));
      }
    }

    const parcelConfigObjectGetterPromise = loadApp(app, userConfiguration, lifeCycles);

    if (container) {
      if ($$cacheLifecycleByAppName) {
        appConfigPromiseGetterMap.set(name, parcelConfigObjectGetterPromise);
      } else {
        const xpath = getContainerXpath(container);
        if (xpath) appConfigPromiseGetterMap.set(`${name}-${xpath}`, parcelConfigObjectGetterPromise);
      }
    }

    return (await parcelConfigObjectGetterPromise)(container);
  };

问题就出在了getContainerXpath(container)这里了,因为页面切换了我们应该触发全新的流程。所以我们的解决方案就围绕如何让该函数返回false了。

  const getContainerXpath = (container: string | HTMLElement): string | void => {
    const containerElement = getContainer(container);
    if (containerElement) {
      查找挂载点是否存在页面中。
      return getXPathForElement(containerElement, document);
    }

    return undefined;
  };

只要我们每次都传一个挂载点是新的且不属于页面的就可以了。 所以最终的解决方案就是 container: document.createElement('div')即可。这样主应用也可以实现渲染了。

总结

  1. 可能会有人为什么不是基于一级路由进行模块切分,这样我们可以直接用自动加载的方式进行。也不会出现上述情况,这个是基于我们业务决定的因为开始改造我们需要适配一级路由不同进行微前端加载。

  2. 加强动手能力,查找了网上资料基本都是解析qiankun的sandbox和动态加载style的源码分析,很少涉及运行全过程的解析。

最后

如果作者有啥不对可以留言,创作不易点个赞呗👍