Basic usage and principle of SingleSpa micro front end

Posted by the elegant on Sun, 20 Feb 2022 13:01:06 +0100

Let's talk about the disadvantages of singleSpa

  • Not flexible enough to dynamically load css files
  • css is not isolated
  • There is no js sandbox mechanism (there is no global object, and the application switched every time is the same window)

The front-end micro touch can be used for a while
qiankun micro front-end framework is very mature and is also implemented based on singleSpa

General implementation idea (those who can't understand the concept of micro front end can understand it by themselves)

  • First register an application in the parent application
  • When the conditions are met (matching path), the script of another sub application will be loaded
  • Loading scripts for sub applications
    – when our sub application is packaged, there are some class libraries on it
  • When the parent application loads into the class library of the child application, some methods on the child application will be called
    – at this time, the class library will put (Mount) the dom of the child application on the parent application
  • Moreover, we should ensure that all paths referenced by the sub application itself are absolute relative to itself
    – otherwise, if some operations of the child application in the parent application call the root path of the parent application, there will be a problem

First create two applications

One child application and each parent application

We need the parent application to load the child application, and we need to export three methods in the child application
Bootstrap mount unmount (provisions of singlespa)

vue projects require npm to install single spa vue
The project of react requires npm to install single spa react

Initialize subapplication

npm installation single spa Vue

  • Configure main js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

// Package for introducing singleSpaVue
import singleSpaVue from 'single-spa-vue'
Vue.config.productionTip = false


//Sub applications cannot be mounted directly
// new Vue({   
//   router,
//   render: h => h(App)
// }).$mount('#app')

// Instead, it is encapsulated into an object
const appOptions = {
    el:'#vue ', / / add an attribute and hang it on the tag with id vue of the parent application
    router,
    render: h => h(App)
}

// Pass in vue and the above object, and the singleSpaVue will return vueLife
// vueLife is a packaged life cycle, corresponding to the three methods of bootstrap mount unmount
const vueLife = singleSpaVue({
  Vue,
  appOptions
})

//Export these three methods  
//I have set these methods for protocol access, and the parent application will call these methods
export const bootstrap = vueLife.bootstrap
export const mount = vueLife.mount
export const unmount = vueLife.unmount
  • We need the parent application to load, and the child application needs to be packaged into LIBS for the parent application to use
  • How to pack it? In Vue config. JS
module.exports = {
  configureWebpack:{
    output:{
       // Give the class library a name
      library:'singleVue',
      // Specifying the module type umd will hang the three properties after packaging on the window 
      //For example, window bootstrap / window. mount / window. oumount
      libraryTarget:'umd' 
    },
    devServer:{
      port:10000 
    }
  }
}

Initialize parent app

npm install single Spa (do not add vue)

  • App. Processing structure in Vue
<template>
  <div id="app">
    <!-- Not written in the route/vue This path indicates that the route cannot be matched, but it can be matched to load sub applications  -->
    <router-link to="/vue">load vue Sub application</router-link>
    <!-- this id = vue Sub application main.js in el Mounted#vue -->
    <div id="vue"></div>
  </div>
</template>
  • main.js processing
import Vue from 'vue'
import App from './App.vue'
import router from './router'
// Fixed export two methods: register application / start application
import {registerApplication,start} from 'single-spa'
Vue.config.productionTip = false

// Register application parameter 1 register a name parameter 2, which is a promise function
registerApplication('myVueApp',
    async()=>{
      // If the path is / vue, the current method will be called, but the method must export the three methods under the sub application (an error will be reported if it is not exported) 
      // But where are the three methods? Please look at the following pictures. The specific writing method is as follows
      // Dynamically create a script tag to introduce this module (the loading order is to load the public first and then their own)
      await loadScript('http://localhost:10000/js/chunk-vendors.js')
      await loadScript('http://localhost:10000/js/app.js')
      // In this way, you can export the lib package on the window. 'singleVue' is Vue config. Package name of JS configuration 
      return window.singleVue //bootstrap mount onmount
      
    },
    // Parameter 3 the user needs to load the sub application just defined when switching to the / vue path
    location=>location.pathname.startsWith('/vue')
)

// Handle promise of parameter 2 above
async function loadScript(url){
  // js loading is asynchronous, so promise should be used
  return new Promise((resole,reject)=>{
    let script = document.createElement('script')
    script.src = url
    script.onload = resole // Loading succeeded
    script.onerror = reject //Loading failed
    document.head.appendChild(script) //Put the script in the head tag of html 
  })
}

// Open application
start()


new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

  • This opens the basic embedded sub application
  • After clicking the button (but there are still problems)
  • css is not isolated (the tag is centered when the css of the sub application is used)
  • Click the route jump error of the sub application
    – the / vue on the path will disappear (click the route of the child application, but jump to the route of the parent application)
    – a virtual path is required in the sub application

Configure basic path for sub route

  • In router/index of sub application
const router = new VueRouter({
  mode: 'history',
  // base: process.env.BASE_URL, / / delete the original
  //When clicking the route of the sub application, you need to load it through / vue
  base: '/vue', 
  routes
})
  • But there is another problem. Every time you click a child route, the route on the parent application is loaded
    – when we need to operate the sub application, we match the path of the sub application itself
    – solution: each route we request must load its own root path
    -In the sub application's main JS
    Mainly look at the new code after if(window.singleSpaNavigate) {}
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import singleSpaVue from 'single-spa-vue'
Vue.config.productionTip = false


const appOptions = {
    el:'#vue',  
    router,
    render: h => h(App)
}

const vueLife = singleSpaVue({
  Vue,
  appOptions 
})

//The following judgment is added
//If the parent application references me
if(window.singleSpaNavigate){
  // Dynamically set a property and add a directory when packaging. The directory is its own root path
  // In this case, when we send a request, we will spell this path to the front to become an absolute path
  __webpack_public_path__ = 'http://localhost:10000/'
}
//We also need to make the child application run independently (if the parent application does not reference me)
if(!window.singleSpaNavigate){
  // If the child application runs independently, it is initialized vue normally, and the el that mounts the parent application can be deleted
  delete appOptions.el
  //vue can be initialized normally  
  new Vue(appOptions).$mount('#app')
}

export const bootstrap = vueLife.bootstrap
export const mount = vueLife.mount
export const unmount = vueLife.unmount

Now, the parent application and nested child application have been basically implemented

And sub applications can also run independently


Topics: Javascript Front-end Vue Vue.js Webpack