A link to the

This paper is a recent analysis of single-SPA, and all the articles are as follows:

  1. An in-depth analysis of single-SPA — navigation events and Reroute
  2. In-depth analysis of Single-SPA — Startup and application management
  3. An in-depth look at single-SPA — the event mechanism
  4. Additional in-depth analysis of module mechanisms, life cycles, and microfront-end types is ongoing

Application is an important part of single-SPA. When running single-SPA, it involves registering/unregistering and uninstalling the Application. This article will provide an in-depth analysis of the startup of single-SPA itself and the management of applications within it.

Single – start of spa

Let’s start with an example of single-SPA code:

// single-spa-config.js
import { registerApplication, start } from 'single-spa';

// Simple usage
registerApplication(
  'app2'.() = > import('src/app2/main.js'),
  (location) = > location.pathname.startsWith('/app2'),
  { some: 'value'});// Config with more expressive API
registerApplication({
  name: 'app1'.app: () = > import('src/app1/main.js'),
  activeWhen: '/app1'.customProps: {
    some: 'value',}}); start();Copy the code

Among them, we can see:

  • Single-spa exports both registerApplication and start methods

  • Start is used to start the single-SPA application. Before that, we can also register the micro front end application with the registerApplication,

  • RegisterApplication is used to register the microfront-end application in single-SPA so that single-SPA knows when/how to initialize, load, and unload the application

start

The code for the start section of single-SPA is as follows:

export function start(opts) {
  started = true;
  if (opts && opts.urlRerouteOnly) {
    setUrlRerouteOnly(opts.urlRerouteOnly);
  }
  if(isInBrowser) { reroute(); }}Copy the code

Here we can see:

  • startAccept an OPTS object as a parameter, where the only configuration item is urlReouteOnly; If urlReouteOnly is set to true, thenhistory.pushState()history.replaceState()Will not triggerreroute
  • If in a browser environment, callstartWould triggerrerouteThe implementation of the

Application management

The management of micro-front-end applications in single-SPA mainly involves:

  • After you register an application, you can configure the application name and activation time
  • If an application is unregistered, the corresponding application will be unregistered

Application registerApplication

Execute the process

To use the registerApplication, we can look directly at the code:

export function registerApplication(appNameOrConfig, appOrLoadApp, activeWhen, customProps) {
  const registration = sanitizeArguments(
    appNameOrConfig,
    appOrLoadApp,
    activeWhen,
    customProps
  );

  if(getAppNames().indexOf(registration.name) ! = = -1)
    throw Error(
      formatErrorMessage(
        21,
        __DEV__ &&
          `There is already an app registered with name ${registration.name}`,
        registration.name
      )
    );

  apps.push(
    assign(
      {
        loadErrorTime: null.status: NOT_LOADED,
        parcels: {},
        devtools: {
          overlays: {
            options: {},
            selectors: [],
          },
        },
      },
      registration
    )
  );

  if(isInBrowser) { ensureJQuerySupport(); reroute(); }}Copy the code

The registerApplication function takes four arguments:

  • appNameOrConfig: Application name, which must be globally unique. If you register an application with the same name twice, an error will be thrown
  • appOrLoadApp: the definition of the application used to load the application, which can be an object containing the single-SPA life cycle/method of loading the application
  • activeWhen: The function that matches the application — activity function or the path that needs to be matched to determine whether the application should be activated
  • customProps: Custom attributes passed to the application

UnregisterApplication — unregisterApplication

UnregisterApplication is the opposite of registerApplication, but is much simpler:

  • Determine whether the application is registered. If not registered, an error is thrown
  • If registered, unloadApplication is called and the application is removed from the apps list
Execution process:

The code is as follows:
export function unregisterApplication(appName) {
  if (apps.filter((app) = > toName(app) === appName).length === 0) {
    throw Error(
      formatErrorMessage(
        25,
        __DEV__ &&
          `Cannot unregister application '${appName}' because no such application has been registered`,
        appName
      )
    );
  }
  return unloadApplication(appName).then(() = > {
    const appIndex = apps.map(toName).indexOf(appName);
    apps.splice(appIndex, 1);
  });
}
Copy the code

UnloadApplication – unloadApplication

At the end of unregisterApplication, unloadApplication is called. After uninstalling the app, it is restored to the NOT_LOADED state and will need to be reloaded the next time it is activated.

Execute the process

The source code

export function unloadApplication(appName, opts = { waitForUnmount: false }) {
  if (typeofappName ! = ="string") {
    throw Error(
      formatErrorMessage(
        26,
        __DEV__ && `unloadApplication requires a string 'appName'`)); }const app = find(apps, (App) = > toName(App) === appName);
  if(! app) {throw Error(
      formatErrorMessage(
        27,
        __DEV__ &&
          `Could not unload application '${appName}' because no such application has been registered`,
        appName
      )
    );
  }

  const appUnloadInfo = getAppUnloadInfo(toName(app));
  if (opts && opts.waitForUnmount) {
    // We need to wait for unmount before unloading the app

    if (appUnloadInfo) {
      // Someone else is already waiting for this, too
      return appUnloadInfo.promise;
    } else {
      // We're the first ones wanting the app to be resolved.
      const promise = new Promise((resolve, reject) = > {
        addAppToUnload(app, () = > promise, resolve, reject);
      });
      returnpromise; }}else {
    /* We should unmount the app, unload it, and remount it immediately. */

    let resultPromise;

    if (appUnloadInfo) {
      // Someone else is already waiting for this app to unload
      resultPromise = appUnloadInfo.promise;
      immediatelyUnloadApp(app, appUnloadInfo.resolve, appUnloadInfo.reject);
    } else {
      // We're the first ones wanting the app to be resolved.
      resultPromise = new Promise((resolve, reject) = > {
        addAppToUnload(app, () = > resultPromise, resolve, reject);
        immediatelyUnloadApp(app, resolve, reject);
      });
    }

    returnresultPromise; }}Copy the code

During the uninstallation process, we can see the following two functions called:

  • addAppToUnload: By adding the application toappToUnload, and wait for follow-up procedure
  • immediatelyUnloadApp: Immediately calls the and in the lifecycle methodunmountandunloadMethods to uninstall the application

addAppToUnload

Applications that need to unload are added to appToUnload for later processing

export function addAppToUnload(app, promiseGetter, resolve, reject) {
  appsToUnload[toName(app)] = { app, resolve, reject };
  Object.defineProperty(appsToUnload[toName(app)], "promise", {
    get: promiseGetter,
  });
}
Copy the code

immediatelyUnloadApp

Chain calls toUnmountPromise and toUnloadPromise to unload the application

Execute the process

function immediatelyUnloadApp(app, resolve, reject) {
  toUnmountPromise(app)
    .then(toUnloadPromise)
    .then(() = > {
      resolve();
      setTimeout(() = > {
        // reroute, but the unload promise is done
        reroute();
      });
    })
    .catch(reject);
}
Copy the code

The resources

Configuration