前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微前端06 : single-spa的注册机制

微前端06 : single-spa的注册机制

作者头像
杨艺韬
发布2022-09-27 14:19:46
4340
发布2022-09-27 14:19:46
举报
文章被收录于专栏:前端框架源码剖析

“在前面的5篇文章中,我们对乾坤进行了比较深入的介绍,但是无论怎么深入都是不全面的,甚至某种意义上来讲乾坤并不是一个微前端框架,single-spa才是,乾坤只是一个对single-spa进行增强的一个方案。接下来的几篇文章主要对single-spa的一些核心机制和功能从源码层面对其进行分析。本文主要分析single-spa的注册机制。 ”

registerApplication的主要逻辑

我先来看single-spa暴露的注册函数的主要逻辑:

代码语言:javascript
复制
// 代码片段1
export function registerApplication(
  appNameOrConfig,
  appOrLoadApp,
  activeWhen,
  customProps
) {
  // 关键点1
  const registration = sanitizeArguments(
    appNameOrConfig,
    appOrLoadApp,
    activeWhen,
    customProps
  );
  // 这里省去许多逻辑...
  // 关键点2
  apps.push(
    assign(
      {
        loadErrorTime: null,
        status: NOT_LOADED,
        parcels: {},
        devtools: {
          overlays: {
            options: {},
            selectors: [],
          },
        },
      },
      registration
    )
  );

  if (isInBrowser) {
    // 关键点3
    ensureJQuerySupport();
    // 关键点4
    reroute();
  }
}

从整体上看,registerApplication一共做了4件比较重要的事情。首先,是对参数进行处理,对应代码片段1中的关键点1,参数处理函数sanitizeArguments有几十行代码,具体怎么处理的,逻辑相对简单,这里就不描述了。对参数的合理处理,给用户提供了更多的灵活性,可以通过不同形式来传递参数,然后将不同格式的参数处理成统一格式。同时,对参数进行了校验。这种写法很常见,在我们日常编程中可以借鉴。其次,是将微应用保存到数组apps中,apps是一个全局变量,会存放所有的注册过的微应用。这个数组很重要,微应用的各种状态都保存在这里,实际上single-spa的核心工作就是对apps中保存的微应用进行管理和控制。再次,是调用ensureJQuerySupport函数对JQuery的某些监听事件进行拦截,下文中进行详述。最后,是调用reroute函数,主要是加载微应用,下文中会进行详述。

ensureJQuerySupport

我看先看ensureJQuerySupport函数的逻辑:

代码语言:javascript
复制
// 代码片段2
export function ensureJQuerySupport(jQuery = window.jQuery) {
    // 这里省略一些代码...
    const originalJQueryOn = jQuery.fn.on;
    const originalJQueryOff = jQuery.fn.off;

    jQuery.fn.on = function (eventString, fn) {
      return captureRoutingEvents.call(
        this,
        originalJQueryOn,
        window.addEventListener,
        eventString,
        fn,
        arguments
      );
    };

    jQuery.fn.off = function (eventString, fn) {
      // 这里省略许多代码... 与jQeury.fn.on类似,不在此赘述
    };
    // 这里省略一些代码...
}

代码片段2中省略了许多逻辑判断,但核心功能可以理解为做了两件事。一是保存了jQuery的事件监听和事件取消函数。二是对jQuery的事件监听函数进行了拦截,具体怎么拦截的,让我们进入captureRoutingEvents函数中一探究竟。

captureRoutingEvents

代码语言:javascript
复制
// 代码片段3
function captureRoutingEvents(
  originalJQueryFunction,
  nativeFunctionToCall,
  eventString,
  fn,
  originalArgs
) {
  if (typeof eventString !== "string") {
    return originalJQueryFunction.apply(this, originalArgs);
  }
  const eventNames = eventString.split(/\s+/);
  eventNames.forEach((eventName) => {
    if (routingEventsListeningTo.indexOf(eventName) >= 0) {
      // 关键点1
      nativeFunctionToCall(eventName, fn);
      eventString = eventString.replace(eventName, "");
    }
  });

  if (eventString.trim() === "") {
    // 关键点2
    return this;
  } else {
    return originalJQueryFunction.apply(this, originalArgs);
  }
}

还记得我们上文保存的originalJQueryFunction函数吗,在函数captureRoutingEvents有了体现。可以概括为该函数在某些条件下执行jQuery.fn.on未被重写前的逻辑。否则就返回this。相对于代码片段2中jQuery.fn.on中的调用,关键点1处的代码,相当于执行了window.addEventListener("hashchange"|"popstate",()=>{})。当然里面利用了些条件逻辑,如果监听的事件不仅仅只有hashchange、popstate两个事件,则继续调用jQuery.fn.on未被重写前的逻辑进行事件的监听。由于我没研究过jQeury,调用被重写前的jQuery.fn.on函数会发生什么,并不太清楚,不过对于理解single-spa而言,能理解上文呈现的逻辑就足够了,对于关键点2处返回this有什么用途,后续遇见该逻辑再进行剖析。

reroute

聊完了ensureJQuerySupport,是时候探索reroute了。reroute函数有将近300行代码,对其中次要逻辑进行删减,且只留下和注册相关的逻辑,如下所示:

代码语言:javascript
复制
// 代码片段4
export function reroute(pendingPromises = [], eventArguments) {
  // 这里省略掉许多代码...
  const {
    appsToUnload,
    appsToUnmount,
    appsToLoad,
    appsToMount,
  } = getAppChanges();
  // 这里省略掉许多代码...
  return loadApps();
  
  // 这里省略掉许多代码...

  function loadApps() {
    return Promise.resolve().then(() => {
      const loadPromises = appsToLoad.map(toLoadPromise);

      return (
        Promise.all(loadPromises)
          .then(callAllEventListeners)
          .then(() => [])
          .catch((err) => {
            callAllEventListeners();
            throw err;
          })
      );
    });
  }
  // 这里省略许多代码...
}

代码片段4中留下了reroute函数的核心逻辑,做了两件事情,一是获取处于各种状态的微应用。二是返回函数loadApps的执行结果。而loadApps中做了一件重要的事情,就是调用了这样一行代码const loadPromises = appsToLoad.map(toLoadPromise);,我们不难知道appsToLoad代表着需要加载的微应用,而toLoadPromise主要完成什么功能呢?请看下文讲解。

toLoadPromise

代码语言:javascript
复制
// 代码片段5,为了精简逻辑,除了省略一些代码,还做了微调
export function toLoadPromise(app) {
  return Promise.resolve().then(() => {
    // 这里省略许多代码...
    return (app.loadPromise = Promise.resolve()
      .then(() => {
        const loadPromise = app.loadApp(getProps(app));
        // 这里省略许多代码...
        return loadPromise.then((val) => {
          // 这里省略许多代码...
          app.status = NOT_BOOTSTRAPPED;
          app.bootstrap = flattenFnArray(appOpts, "bootstrap");
          app.mount = flattenFnArray(appOpts, "mount");
          app.unmount = flattenFnArray(appOpts, "unmount");
          app.unload = flattenFnArray(appOpts, "unload");
          app.timeouts = ensureValidAppTimeouts(appOpts.timeouts);

          delete app.loadPromise;

          return app;
        });
      })
      .catch((err) => {
        // 这里省略许多代码...
      }));
  };
}

代码片段5中原本有100多行代码,对其进行精简,我们发现核心逻辑其实只做了三件事,一是调用子应用传入的加载微应用的方法。二是保存微应用的各种状态。三是规范化子应用暴露的方法。其实看源码就要抓住核心逻辑,其他疑惑都会迎刃而解,忌讳胡子眉毛一把抓,逐行阅读,最终会把自己陷在里面,不知道代码究竟想要表达什么,进而丧失了继续读下去的热情。好了,关于single-spa的注册机制今天就分析到这里,请朋友们期待更多关于single-spa的文章。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 杨艺韬 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • registerApplication的主要逻辑
  • ensureJQuerySupport
  • captureRoutingEvents
  • reroute
  • toLoadPromise
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档