Demand background


  1. At the beginning of the project, school management and main site (i.e. mall) were two separate projects, which were independently deployed and accessed through their respective domain names. Users clicked the TAB of “School Management” to open a new window to browse the page.
  2. In one demand, the product required “school management” to be accessed under the TAB of the current page of the main site. Due to a tight schedule, iframe was adopted to embed it in the main site.
  3. Because every time when switching from other TAB pages to ‘School Management’, there will always be a blank screen loading time, so in the later period, the implementation of the micro front end is changed.

Renderings of both implementations


1. The iframe approach

2. Single – spa

Advantages and disadvantages of iframe and single-SPA


The advantages of the iframe

  1. Fully isolated CSS and JS, avoiding style and JS contamination between systems.
  2. The subsystem can be embedded without modification at all.

The disadvantage of the iframe

  1. Page loading problem: affecting the loading of the main page, blocking the onload event, loading itself is also very slow, too much page cache will lead to computer lag.
  2. Layout problem: Iframe must be given a specified height or it will collapse. Solution: The subsystem calculates the height in real time and sends it to the home page via postMessage. The home page dynamically sets the height and modifs the subsystem or proxy insertion script. In some cases, multiple scrollbars appear and the user experience is not good.

The advantages of single – spa

  1. Fast loading, you can extract all the modules shared by the system to achieve on-demand loading, one load, and other reuse.
  2. User experience is good, fast, content changes do not need to reload the entire page, avoiding unnecessary jumps and repeated rendering.
  3. Fewer HTTP requests and less server stress.

The disadvantage of single – spa

  1. CSS and JS need to be standardized and isolated. Otherwise it is easy to cause global contamination, especially for vUE’s global components, global hooks.
  2. The subsystem needs a small amount of change, which does not affect the independent development and deployment of the subsystem and its functions.

Single – an overview of the spa


Single-spa is a javascript micro-front-end framework that aggregates multiple single-page applications into a Single application. It includes the following:

  1. Applications, each application itself is a complete SPA (sort of). Every application can respond to URL-routing events and must know how to initialize, mount, and uninstall itself from the DOM. The main difference between a traditional SPA application and a Single SPA application is that they must be able to coexist with other applications, and they do not have separate HTML pages.
  2. A single-SPa-config configuration, which is the HTML page and JavaScript to register the application with Single SPA. Each application registers three things.
  • A name
  • A function (code to load the application)
  • A function (Determine when the application is active/inactive)

The realization of the single – spa


Parent project processing

  1. Depend on installation.
  yarn add single-spa -D
Copy the code
  1. Add a route to router.js.
{ path: 'schoolManage*', name: 'schoolManage', component: schoolManage, meta: { role: [' Principal ', 'Academic Affairs ',' Magic Supervisor ', 'Magic Supervisor - School Supervisor ',' School Supervisor ', 'AI Supervisor '], keepAlive: true}, beforeEnter: requirePermission}Copy the code
  1. Add the single-sp-confing.js file in the SRC directory.
SingleSpa. RegisterApplication (/ / registration services' singleSchool ', async () => { let singleSchool = null await getManifest(`${JIAOWU_URL}/manifest.json? v=${new Date().getTime()}`, 'app').then(() => { singleSchool = window.singleSchool }) return singleSchool }, Location = > location. The pathname. StartsWith ('/schoolManage ') / / configure micro front-end module prefix) / * * getManifest: Load the manifest.json file remotely, * */ const getManifest = (url, bundle) => new Promise(async (resolve) => { const { data } = await axios.get(url) const { entrypoints, publicPath } = data const assets = entrypoints[bundle].assets for (let i = 0; i < assets.length; i++) { await runScript(publicPath assets[i]).then(() => { if (i === assets.length - 1) { resolve() } }) } }) const runScript = async (url) => { return new Promise((resolve, reject) => { const script = document.createElement('script') script.src = url script.onload = resolve script.onerror = reject const firstScript = document.getElementsByTagName('script')[0] firstScript.parentNode.insertBefore(script, firstScript) }) }Copy the code

Subproject processing

  1. Install dependencies
  yarn add single-spa-vue -D
Copy the code
  1. Introduce single-SPa-vue in main.js.
const vueOptions = { el: '#school-container', router, store, render: h => h(App), data: { isViewSidebar: Const vueLifecycles = singleSpaVue({vue, appOptions:) const vueLifecycles = singleSpaVue VueOptions}) export const bootstrap = vueLifecycles. Bootstrap // export const mount = vueLifecycles Export const unmount = vuelifecycles. unmountCopy the code

The processing of webpack

  1. Modify the vue.config.js file to mount the exported microfront-end object to the window.
  configureWebpack: (config) => {
    const buildObj = {
      name: name,
      output: {
        library: 'singleSchool',
        libraryTarget: 'window'
      }
    }
  }
Copy the code
  1. Install the stats-webpack-plugin dependency package, then modify the Plugin configuration of Webpack to generate the manifest.json file (a static resource file directory table generated by packaging).
  plugins: [
        new StatsPlugin('manifest.json', {
          chunkModules: false,
          entrypoints: true,
          source: false,
          chunks: false,
          modules: false,
          assets: false,
          children: false,
          exclude: [/node_modules/]
        })
  ]
Copy the code
  1. Because domain names vary in different environments, you need to modify publicPath configuration.
  publicPath: process.env.NODE_ENV === 'development' ? '//localhost:8082/' : process.env.VUE_APP_PUBLIC_PATH,
Copy the code
  1. Style isolation
  • Install the postCSs-selector -namespace plug-in.
  yarn add postcss-selector-namespace -D
Copy the code
  • Added plugin configurations in PostCSS.
  css: {
    extract: false,
    loaderOptions: {
      sass: {
        prependData: `
        @import "@/styles/variables.scss";
        `
      },
      postcss: {
        plugins: [
          require('postcss-selector-namespace')({
            namespace (css) {
              if (css.includes('noNamespace.css')) {
                return ''
              }
              return '.single-spa-school'
            }
          })
        ]
      }
    }
  }
Copy the code

Communication between projects

1. Abandonment method: postMessage method provided by H5 is adopted for communication, which needs to be optimized in the later stage. For example, page jumps between subprojects:

created () { if (! window.ADD_LISTENNER_MESSAGE) { window.addEventListener('message', this.handleUrl, false) } } methods: { handleUrl (data) { if (data.data && data.data.fun && data.data.type === 'router') { let query = data.data.query ? data.data.query : {} this.$router.push({ name: data.data.fun, query }) } } }Copy the code
 window.postMessage({fun: 'setting.balance', type: 'router'}, '*')
Copy the code

2. New method: Pass eventHub from the main project (passed in the fourth parameter in the method of registering microservices) to the sub-project, listen for the time in the main project, and trigger the time in the sub-project.

SingleSpa. RegisterApplication (/ / registration services' singleSchool ', async () => { let singleSchool = null await getManifest(`${JIAOWU_URL}/manifest.json? v=${new Date().getTime()}`, 'app').then(() => { singleSchool = window.singleSchool }) return singleSchool }, Location = > location. The pathname. StartsWith ('/schoolManage), / / configuration micro front-end module prefix {eventHub})Copy the code