两只小蚂蚁

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Single-SPA 源码分析

//src/application/apps.js
export function registerApplication(
  appNameOrConfig,
  appOrLoadApp,
  activeWhen,
  customProps
) {
  const registration = sanitizeArguments(
    appNameOrConfig,
    appOrLoadApp,
    activeWhen,
    customProps
  );
  //把注册的spa放到apps数组
  apps.push(
    assign(
      {...},
      registration
    )
  );

  if (isInBrowser) {
    ensureJQuerySupport();
    reroute();
  }
}

//src/navigation/reroute.js
export function reroute(pendingPromises = [], eventArguments) {
...
	const {
    appsToUnload,
    appsToUnmount,
    appsToLoad,
    appsToMount,
  } = getAppChanges();
  //是否已启动
  if (isStarted()) {
    appChangeUnderway = true;
    appsThatChanged = appsToUnload.concat(
      appsToLoad,
      appsToUnmount,
      appsToMount
    );
    return performAppChanges();
  } else {
    appsThatChanged = appsToLoad;
    //未启动的话就加载app
    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;
          })
      );
    });
  }
}
//src/lifecycles/load.js
export function toLoadPromise(app) {
  return Promise.resolve().then(() => {
  	...
    app.status = LOADING_SOURCE_CODE;
    return (app.loadPromise = Promise.resolve()
      .then(() => {
        //调用每个spa的app方法,loadApp就是app方法
        //registration.loadApp = appNameOrConfig.app;
        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;
        });
      })
   });
}

到现在为止toMountApp数组就准备好了,等待调用start方法。

//src/start.js
export function start(opts) {
  //把全局状态改为started
  started = true;
  if (opts && opts.urlRerouteOnly) {
    setUrlRerouteOnly(opts.urlRerouteOnly);
  }
  //又调用reroute,不过这次分支不同了
  if (isInBrowser) {
    reroute();
  }
}

//src/navigation/reroute.js
export function reroute(pendingPromises = [], eventArguments) {
  ...
  //是否已启动
  if (isStarted()) {
    appChangeUnderway = true;
    appsThatChanged = appsToUnload.concat(
      appsToLoad,
      appsToUnmount,
      appsToMount
    );
    return performAppChanges();
  }
  
  function performAppChanges() {
    return Promise.resolve().then(() => {
      //事件通知
      window.dispatchEvent(
        ...
      );
      const unloadPromises = appsToUnload.map(toUnloadPromise);
      const unmountUnloadPromises = appsToUnmount
        .map(toUnmountPromise)
        .map((unmountPromise) => unmountPromise.then(toUnloadPromise));
      ...
      const loadThenMountPromises = appsToLoad.map((app) => {
        return toLoadPromise(app).then((app) =>
          //调用bootstrap事件
          tryToBootstrapAndMount(app, unmountAllPromise)
        );
      });
      const mountPromises = appsToMount
        .filter((appToMount) => appsToLoad.indexOf(appToMount) < 0)
        .map((appToMount) => {
          return tryToBootstrapAndMount(appToMount, unmountAllPromise);
        });
      return unmountAllPromise
        .catch((err) => {
          callAllEventListeners();
          throw err;
        }).then(() => {...});
    });
}

function tryToBootstrapAndMount(app, unmountAllPromise) {
  if (shouldBeActive(app)) {
    return toBootstrapPromise(app).then((app) =>
      unmountAllPromise.then(() =>
        shouldBeActive(app) ? toMountPromise(app) : app
      )
    );
  } else {
    return unmountAllPromise.then(() => app);
  }
}

    

流程图如下:

Single-SPA MFE注册

singleSpa.registerApplication(
    appName: string,
    applicationOrLoadingFn: () => <Function | Promise>,
    activityFn: (location) => boolean,
		customProps?: Object =
)
//demo
singleSpa.registerApplication({ 
  name:"app1", 
  app:() => import('./app1/app1.js');, 
  activeWhen:'/app1'
});

Single-SPA 能力分析

从原理和基础来看, Single-SPA提供了根据URL对微应用调度和生命周期钩子挂载通知的能力。
但同时,以下功能需要使用者自己考虑或实现:

  • 微应用的加载
  • 微应用的隔离
  • 应用间通信
  • CSS污染问题

所以SSPA不是一个可以直接落地使用的微前端框架,需要对其进行二次开发或封装。

posted on 2021-12-27 18:27  两只小蚂蚁  阅读(64)  评论(0编辑  收藏  举报