Other micro front Vue3 single-SPA

A taste of micro front, to poor thousands of miles of eyes

1. About the micro front end

A micro front end is a process of applying the routing of a page or a module (making it a separate application, deployed separately) and putting it together. This is what I think of as a micro front end. It’s kind of like an iframe

1.1 Why not use itiframe

In fact, if you carefully read the latest popular micro front-end framework – Qiankun framework, its documentation has a description, here to emphasize

  1. urlOut of sync. Browser refreshiframe.urlLoss of status, back forward button not available.
  2. UIOut of sync,DOMStructures are not shared.
  3. The global context is completely isolated and memory variables are not shared. To meet the requirements for communication and data synchronization between the internal and external systems of the iframe, the cookies of the primary application must be transmitted to the sub-applications with different root domain names to achieve the login exemption effect.
  4. Slow. Each subapplication entry is a browser context reconstruction and resource reloading process.

1.2 Micro front-end application communication

  1. The communication is based on URL, but the messaging capability is weak.
  2. Based on theCustomEventAchieve communication.
  3. Based on thepropsCommunication between master and sub-applications.
  4. Using global variables,ReduxTo communicate.

2. Single-SPA

Is a JavaScript front-end solution based on front-end microservization. There is no style isolation, JS execution isolation, only route hijacking and application loading

Next, try single-SPA

2.1 Creating a Project

  1. Create two vUE projects (the simpler the better), one for the child and one for the main application (also known as the base)
  2. Install single-SPA, yarn add single-SPa-vue-s in the sub-application
  3. Make the changes on the child application main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import singleSpaVue from 'single-spa-vue';

Vue.config.productionTip = false


const appOptions = {
  el:'#vue-p'.// Mount a tag to the parent application whose ID is vue-p,
  router,
  store,
  render: h= > h(App)
}

const vueLifeCycle = singleSpaVue({
  Vue,
  appOptions:appOptions
})

// Determine the parent application reference
if(window.singleSpaNavigate){
  __webpack_public_path__ = 'http://localhost:10001/'
}
if(!window.singleSpaNavigate){
  // const appOptions = Object.assign()
  delete appOptions.el
  new Vue(appOptions).$mount('#app')}// Protocol access, defined protocols, parent applications will call these methods

export const bootstrap = vueLifeCycle.bootstrap;
export const mount = vueLifeCycle.mount;
export const unmount = vueLifeCycle.unmount;
Copy the code

Note that the three methods must be exported for protocol access: A. bootstrap B. mount C. unmount

2.2 Basic configuration of sub-applications

The main application loads the child application and packages the child application to generate a lib for the parent application to use. Here, you need to configure the webpack packaging of the child application

module.exports = {
    configureWebpack: {output: {library:'singleVue'.// Mount the singleVue package to the window
            / / this is equivalent to the window. The singleVue. The bootstrap/mount/unmount to
            libraryTarget:'umd'}},devServer: {// Cross domains are required because different sub-applications and primary applications are deployed at different addresses
        headers: {
            'Access-Control-Allow-Origin': The '*',},port: '10001'}}Copy the code

2.3 Basic Configuration of active Applications

  • First of all inApp.vuePut the DOM that the child application needs to mount,<! <div id="vue-p"></div>
  • The installationsingle-spalibrary
  • inmain.jsAnd register the sub-applications as follows:
import { registerApplication,start } from 'single-spa'
// Mount the script function
async function loadScript(url){
  return new Promise((resolve,reject) = >{
    let scrpit = document.createElement('script');
    scrpit.src = url;
    scrpit.onload = resolve;
    scrpit.onerror = reject;
    document.head.appendChild(scrpit);
  })
}

registerApplication(
  'myVueApp'.async() = > {console.log('Load module');
    loadScript('http://localhost:10001/js/app.js');
    loadScript('http://localhost:10001/js/chunk-vendors.js');
  },
  // This route is loaded when the user switches to /about
  location= > location.pathname.startsWith('/about'),
  {
    params:'This is a parameter that the parent application gives to the child application.'
  }
)

start();
Copy the code

For the above code, do the following explanation of the loaded JS: In the child application, we open the Network of the debugging tool, and it can be clearly seen that the child application will load two important JS files, one is app.js, and the other is chunk-peddlers. Js. Since the parent application calls the child application, it needs to obtain the JS resources of the child application and load them. Therefore, we need to introduce js to the child application

2.4 Single-SPA issues

2.4.1 CSS Isolation Problems

In single-SPA, this problem has not been solved, so we need to solve it by ourselves. The general solution is as follows:

  1. Style isolation between sub-applications can be implemented in the form of dynamic style sheets. When the application is switched, the old application style is removed and the new application style is added

  2. Style isolation between master and child applications

    • BEM: Convention item prefix
    • CSS-Modules packages generate non-conflicting selector names
    • Shadow DOM
    • css-in-js

2.4.2 JS isolation issues (mainly isolating properties on Windows)

  1. Generally, the js sandbox mode is adopted to create a clean environment for the child application to use, and when switching, you can choose to discard properties and restore properties

  2. How to implement JS sandbox

    • The snapshot sandbox will compare the js before and after, save the differences, and when returning, restore the previous js (only for the case of one child application).
    • Agent sandbox, different applications use different agents to handle this

3. Qiankun Micro front frame

Based on single-SPA provides a more out-of-the-box API, technology stack independent

There is not much to say about the universe framework, because the framework itself is very rich in documents, here is a simple to say that there is no document, about the micro application is how to configure Vue3

3.1 Active Application Configuration

In this example, the deployment address is tangjievic.top, so the configuration code is as follows :(in the main application main.js)

letpath = process.env.NODE_ENV ! = ='development'?'https://tangjievic.top':'http://localhost:40001'
registerMicroApps([
    {
      name: 'datamap'.// app name registered
      entry: `${path}/public/childapp/datamap/`.container: '#data-map'.// data-map
      activeRule: '/public/home/datamap',}]); start({sandbox: {strictStyleIsolation: true}});Copy the code

3.2 Sub-application Configuration

Basic configuration steps:

  • Add the public-path.js file to modify the publicPath at run time
  • Write a rendering function where we’re going to get an instance of the application, which in this case is the dataMapApp
  • Case rendering, where the case is rendered as a micro application and the case is rendered as a standalone project
  • The apis for exporting conventions are bootstrap,unmount, and mount
import './public-path';
let dataMapApp:any = null; interface propsRender{ container? :HTMLElement; }let router = null;
let box:any = null
function render(props:propsRender ){
    constcontainer:any = props.container? props.container:null;
    box = container ? (container).querySelector('#app') : '#app'
    router = createRouter({
        history:createWebHashHistory((window as Window & typeof globalThis & {__POWERED_BY_QIANKUN__:string}).__POWERED_BY_QIANKUN__ ? '/public/childapp/datamap/':'/'),
        routes
    })
    dataMapApp = createApp(App);
    dataMapApp.use(router).use(store).mount(box);
}

if(! (window as Window & typeof globalThis & {__POWERED_BY_QIANKUN__:string}).__POWERED_BY_QIANKUN__){
    render({});
}

  
export async function bootstrap() {
    console.log('[vue] vue app bootstraped');
}
export async function mount<F> (props:F) {
    console.log('[vue] props from main framework', props);
    render(props);
}
export async function unmount() {
    dataMapApp.unmount();
    dataMapApp = null as any;
    router = null;
}
Copy the code