originally

Our company has an operating system with roughly 30 separate business systems developed on it. The amount of code is very large, local development will start a configuration not bad computer card into half dead, packaging can only be in the packaging machine, local packaging dare not imagine. It is also a huge project to manage so many business systems code. There may be four or five business lines released at the same time on the same release day, so the acceptance of each release is relatively late because it has to wait for others to release. So in this case, I thought of using a micro front end to extract the corresponding business system code from this bloated system

Technology selection

The original code is developed using DVA, and there is no inconsistency of technology stacks, so there is no framework like SingleSpa that integrates different technology stacks. The project then uses dVA’s asynchronous loading capability, so it just ends up changing the page file loading logic.

The specific implementation

As shown in the figure above, the sub-business system codes are extracted from the original codes and packaged separately. In order to reduce the code redundancy between the subsystem and the parent system, the subsystem uses external to extract public modules (React, ANTD, DVA, etc.). The parent system needs to reference the CDN resources of these modules or export these resources to Windows in advance

// Each submodule is set as required
externals: {
  'react': 'react'.'antd': 'antd'.'dva': 'dva'. },Copy the code

In order to make it easy for the parent system to find the component static resources corresponding to each page, the subsystem is packaged in umD mode. This way, when the parent system references the entry file for each subsystem, the component with the corresponding name can be found in Windows

// Submodule Webpack
output: {
    path: xxx,
    filename: '[name].[chunkhash:8].js'.chunkFilename: 'js/[name].[chunkhash:8].chunk.js'.libraryTarget: 'umd'.library: xxx, // The module name
},
Copy the code

Subsystem entry file

// We can set the publicPath of the js runtime environment to the corresponding publicPath
__webpack_public_path__ = 'xxxxxx';

export default {
  version: '1.0.0'.// Subsystem page file or model file
  moudle: () = > import('xxxx'),};Copy the code

Import from the parent system

/ / insert the js
const insertScript = (e) = > {
  return new Promise((reslove, reject) = > {
    const i = document.createElement('script');
    i.setAttribute('type'.'text/javascript');
    i.setAttribute('src', e);
   
    function onload() {
      if(! (this.readyState && this.readyState ! = ='loaded' && this.readyState ! = ='complete')) {
        i.onload = null; i.onreadystatechange = i.onload; reslove(); }}function onerror(ee) {
      reject(ee);
    }
    i.onreadystatechange = onload;
    i.onload = onload;
    i.onerror = onerror;
    document.querySelector('head').appendChild(i);
  });
};

// Component cache
const subappRoutes = [];
// libraryName for each child business system
const library = window['xxx'];
// The entry file address corresponding to each sub-service system can be configured on the server side
const url = 'xxx'

const AsyncComponent = async (pathname) => {
  const id = pathname;
  // Check whether subproject resources are loaded
  let asyncLoaded = false;
  if (subappRoutes[id]) {
    // If the subproject module has already been loaded, it is not loaded
    ayncLoaded = true;
  } else if (library && library[pathname]) {
    // Load the corresponding component
    const res = await library[pathname]();
    subappRoutes[id] = res.default;
    asyncLoaded = true;
  } else {
    try {
      await insertScript(url);
      if (library && library[pathname]) {
        const res = await library[pathname]();
        subappRoutes[id] = res.default;
        asyncLoaded = true; }}catch (error) {
      // Error handling}}return asyncLoaded ? subappRoutes[id] : ERROR;
};

export default[{name: 'xxx'.path: 'xxx'.models: () = > AsyncComponent('xxx'),
    component: () = > AsyncComponent('xxx'),},... ] ;Copy the code

conclusion

The above operation can separate the child business system from the parent system, and the advantage of SingleSpa is that there is no need to reference additional framework, no more code development, except the route import method needs to be modified, subsystems need to be extra packaged, reducing the workload. And the development is the same as before. The downside is that two projects need to be started at a time, and subsystems cannot exist independently of the parent system