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