QianKun 2.0 微前端源碼解析

此篇文章只簡(jiǎn)單梳理思路,不會(huì)源碼逐行分析,看此文章前請(qǐng)熟悉qiankun和single-spa的使用最好

暴露出的API, 從index.ts文件可以看出下面暴露的API

export { loadMicroApp, registerMicroApps, start } from './apis';
export { initGlobalState } from './globalState';
export * from './errorHandler';
export * from './effects';
export * from './interfaces';
export { prefetchApps } from './prefetch';

1. 注冊(cè)子應(yīng)用 registerMicroApps

注冊(cè)子應(yīng)用的接口, 基本原理是內(nèi)部有一個(gè)子應(yīng)用數(shù)組,可以注冊(cè)多個(gè)子應(yīng)用,遍歷數(shù)組調(diào)用single-sparegisterApplication函數(shù)進(jìn)行注冊(cè), 此API用法如下, 可以參考 https://single-spa.js.org/docs/api

registerApplication

注意此函數(shù)的第二個(gè)參數(shù)applicationOrLoadingFn,返回一個(gè)resolved的應(yīng)用或者一個(gè)promise,注意這里的 the resolved application其實(shí)是single-spa定義的一個(gè)概念,一個(gè)應(yīng)用的概念,具體參考https://single-spa.js.org/docs/building-applications
說白了就是要返回一個(gè)如下的對(duì)象,帶有名字,聲明周期方法, 這就是qiankun要求你子應(yīng)用必須聲明這些聲明周期函數(shù)的目的,返回給single-spa

{
  name: "applicationName",
  bootstrap: [ bootstrap function array],
  mount: [ mount function array],
  unmount: [ unmount function array]
}

registerMicroApps里面關(guān)鍵的代碼如下,注意這個(gè)loadApp才是真正封裝的返回應(yīng)用的地方

  unregisteredApps.forEach(app => {
    const { name, activeRule, props, ...appConfig } = app;

    registerApplication({
      name,
      app: async () => {
        await frameworkStartedDefer.promise;
        return loadApp({ name, props, ...appConfig }, frameworkConfiguration, lifeCycles);
      },
      activeWhen: activeRule,
      customProps: props,
    });
  });

加載子應(yīng)用內(nèi)部函數(shù) loadApp

下面著重看下這個(gè)加載子應(yīng)用函數(shù),分幾個(gè)小步驟看下

  1. 加載入口文件 importEntry: 加載指定的html文本,和腳本用來執(zhí)行,這就是你在qiankun中傳入entry: '//localhost:7100'類似參數(shù)的用途,用此路勁來加載你子應(yīng)用的入口文件,和手動(dòng)執(zhí)行html中的腳本
  2. 創(chuàng)建子應(yīng)用DOM render: 如果你注冊(cè)的時(shí)候加入了render方法,此處會(huì)調(diào)用執(zhí)行,否則找到container元素,把渲染的內(nèi)容插入節(jié)點(diǎn)中
  3. 創(chuàng)建沙箱 sandbox: 此處是qiankun的核心,創(chuàng)建了js的沙箱進(jìn)行子應(yīng)用之間和主應(yīng)用的隔離, 此處不展開分析,原理就是window對(duì)象的屬性劫持,內(nèi)部用一個(gè)對(duì)象緩存子應(yīng)用window下面屬性方法,達(dá)到隔離的目的,下面代碼可以看出,支持proxy的就用proxy代理的沙箱隔離,否則用SnapshotSandbox,簡(jiǎn)單來說就是內(nèi)部備份,diff 方式記錄, SingularProxySandbox是老沙箱方式實(shí)現(xiàn),說是穩(wěn)定了再用統(tǒng)一的ProxySandbox
  if (window.Proxy) {
    sandbox = singular ? new LegacySandbox(appName) : new ProxySandbox(appName);
  } else {
    sandbox = new SnapshotSandbox(appName);
  }
  1. 執(zhí)行加載前事件 beforeLoad: 利用execHooksChain函數(shù)歸并執(zhí)行函數(shù),簡(jiǎn)單來說就是類似redux中的compose洋蔥模型,上一次函數(shù)的返回值作為下一個(gè)函數(shù)的參數(shù),包裹執(zhí)行,也是中間件原理,后面的一些加載聲明周期函數(shù)與此類似
  2. 執(zhí)行入口文件的腳本 execScripts: 此步驟就是獲取子應(yīng)用指定的主腳本,執(zhí)行并且獲取子應(yīng)用的bootstrap,mount, unmount等聲明周期函數(shù)
  3. 返回應(yīng)用并執(zhí)行內(nèi)部的一些鉤子周期函數(shù): 此時(shí)就可以返回一個(gè)滿足single-spa的應(yīng)用了,同時(shí)在mount和unmount之前會(huì)執(zhí)行一些內(nèi)部定義的生命周期鉤子

到此為止,基本整個(gè)注冊(cè),加載流程清楚了

2. 動(dòng)態(tài)加載應(yīng)用 loadMicroApp

qiankun提供了動(dòng)態(tài)加載子應(yīng)用的方式,簡(jiǎn)單代碼如下, 動(dòng)態(tài)調(diào)用了single-spamountRootParcel方法和qiankun內(nèi)部loadApp來實(shí)現(xiàn)應(yīng)用加載

export function loadMicroApp<T extends object = {}>(
  app: LoadableApp<T>,
  configuration = frameworkConfiguration,
): MicroApp {
  const { props, ...appConfig } = app;
  return mountRootParcel(() => loadApp(appConfig, configuration), {
    domElement: document.createElement('div'),
    ...props,
  });
}

3. 啟動(dòng)應(yīng)用 start

start的代碼也沒有幾行,如下主要就是調(diào)用single-spa內(nèi)部的start來啟動(dòng)應(yīng)用,對(duì)應(yīng)的為startSingleSpa此函數(shù)

export function start(opts: FrameworkConfiguration = {}) {
  frameworkConfiguration = opts;
  const {
    prefetch = true,
    sandbox = true,
    singular = true,
    urlRerouteOnly,
    ...importEntryOpts
  } = frameworkConfiguration;

  if (prefetch) {
    prefetchApps(microApps, prefetch, importEntryOpts);
  }

  if (sandbox) {
    if (!window.Proxy) {
      console.warn('[qiankun] Miss window.Proxy, proxySandbox will degenerate into snapshotSandbox');
      // 快照沙箱不支持非 singular 模式
      if (!singular) {
        console.error('[qiankun] singular is forced to be true when sandbox enable but proxySandbox unavailable');
        frameworkConfiguration.singular = true;
      }
    }
  }

  startSingleSpa({ urlRerouteOnly });

  frameworkStartedDefer.resolve();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。