The key is that the parent app inserts the child appCopy the code

precondition

  • Both parent and child applications are Vue

The application layer

Stage 1: Basic introduction

The key point
  • Instead of rendering directly, the child application is implemented as a lib package

  • Sub-applications should be packaged according to the spa protocol, i.e

    • The lib is calledsingleVue, the export mode isumd
    • When the main application is introduced, no longernew VueInstead, the configuration is handed over tosingleSpaManage to generateboostrap mount unmountThree exposed interfaces
  • In the parent application startup project, the application is registered, and if a match is made to the child application path, the defined logic is executed (that is, render the child application to the specified area).

implementation

The child application

No longer directlynew VueThe vUE instance is generated, but the exposed interface is generated according to the protocol (that is, let singleSpaVue take over)
import singleSpaVue from 'single-spa-vue';

const appOptions = {
  el: '#vue'.// Mount to the tag vue in the parent application
  router,
  render: h= > h(App)
}

const vueLifeCycle = singleSpaVue({
  Vue,
  appOptions
})
// Expose the interface
export const boostrap = vueLifeCycle.boostrap;
export const mount = vueLifeCycle.mount;
export const unmount = vueLifeCycle.unmount;

Copy the code
Configure Vue to package child application into lib for parent application

SRC Create vue.config.js in the root directory

module.exports = {
    configureWebpack : {
        output: {
            library: 'singleVue'.// Specify the lib name
            libraryTarget: 'umd' // Specify the export format
        },
        devServer: {
            port: 10000 // Specify the service running port}}}Copy the code

The parent application

The parent application registers the child application
import { registerApplication, start } from 'single-spa'; Import router from './router' // import router from './router' // registerApplication('myVueApp',// just a token async () => { Console. log(' Load child Vue application '); }, location => location.hash. StartsWith ('#/vue'), In this case, the user switches to a path starting with /vue. I need to load the child application I just defined.)Copy the code
Implementation effect

Stage 2: Render subapplications

Key points:
  • When the route matches, render the subapplication, which we all do nowAll in JSThe problem then becomes introducing its counterpartvendor.jsandapp.js(Take the simplest VUE project as an example)
    • A child application requires a fixed static resource path to be exported. In this case, the root domain name changes to the domain name of the parent application during dynamic import and cannot be found
    • throughfetchTo obtain the static resources of the child application to complete the rendering
  • Optimization: Support child applications to run independently, that is, children should use traditional applications if not introduced by the parentnew VueForm for easy independent development
implementation

A layer of logical judgment can be made in the child application. If it is in the form of micro-front-end embedding, static resources are set as fixed absolute paths to avoid the situation that the parent application cannot find them when it is introduced

// The singleSpaNavigate attribute is the default singleSpa attribute and its value is a function
if(window.singleSpaNavigate){
  // The logic to set the WebPack static resource path to the child application path should be implemented here
}
// Support child applications to run independently
else {
  delete appOptions.el;
  new Vue(appOptions).$mount('#app');
}
Copy the code

We know that WebPack has a configuration item, publicPath, that sets the path for webPack to output static resources, but we now need dynamic Settings

Look at the Webpack documentation and find the artifact __webpack_public_path__. In a word, this is another way to configure the “publicPath” parameter in the output configuration.

Then, we can perfect the above logic as

// The singleSpaNavigate attribute is the default singleSpa attribute and its value is a function
if(window.singleSpaNavigate){
    // The logic to set the WebPack static resource path to the child application path should be implemented here
  __webpack_public_path__ = 'http://localhost:10000/'
}
// Support child applications to run independently
else {
  delete appOptions.el;
  new Vue(appOptions).$mount('#app');
}
Copy the code
The renderings are as follows

Note the domain name, you can see that we have implemented the requirement to load the child application in the parent application

Stage three: Isolation

CSS isolation
  • Style isolation between child applications
  • Style isolation between master and child applications
    • BEM convention item prefix
    • Css-modules package generates non-conflicting selector names
    • Shadom DOM
    • css-in-js
The specific implementation
shadowDom

Clean isolation is achieved by creating a shadowDom container as a child application, and then making the style effect in shadowDom’s scope

  • Let’s say p is a subapplication
  • Assume styleElm is the style of the child application
 let shadomDOM = document.getElementById('shadow').attachShadow({
            mode: 'closed'
        });

let pElm = document.createElement('p');
    pElm.innerHTML = 'hello zf';
let styleElm = document.createElement('style');
    styleElm.textContent = ` p{color:red} `

    shadomDOM.appendChild(styleElm);
   // shadomDOM.appendChild(pElm);
Copy the code

Problem: Components that exist in third-party libraries (such as ANTD’s Modal) are disabled when mounted to the body

document.body.appendChild(pElm);
Copy the code
The sandbox

Define the sandbox class, intereFace as follows

Active interface {Object proxy; // The real manager of the sandbox environment (current state) Object windowSnapshot; // Snapshot Object (previous state) Object modifyPropsMap; // Record changes on the proxy // 1. Record snapshots 2. In short, apply the modified state of the last snapshot: Update the snapshot and update the sandbox based on the modified state active(){} // 1. 2. Restore the sandbox to the state of the last snapshot. In short: Restore the sandbox to inactive(){}}Copy the code

Instance as follows


        class SnapshotSandbox {
            constructor(){
                this.proxy = window; / / window properties
                this.modifyPropsMap = {}; // Log the changes on the window
                this.active();
            }
            // Update the snapshot and update the sandbox based on the modified state
            active(){
                this.windowSnapshot = {}; 
	              // Easy to take photos (shallow copy)
                for (const prop in this.proxy) {
                    if (Object.hasOwnProperty.call(this.proxy, prop)) {
                        this.windowSnapshot[prop] = this.proxy[prop]; }}// Apply the last change to restore the state
                Object.keys(this.modifyPropsMap).forEach(p= >{
                    this.proxy[p] = this.modifyPropsMap[p]; })}// Update the state based on the snapshot and restore the sandbox
            inactive(){
                for (const prop in this.proxy) {
                    if (Object.hasOwnProperty.call(this.proxy, prop)) {
                        if(this.proxy[prop] ! = =this.windowSnapshot[prop]){
                            this.modifyPropsMap[prop] = this.proxy[prop];
                            this.proxy[prop] = this.windowSnapshot[prop];
                        }
                    }
                }
            }
        }
Copy the code

Eg as follows

 let sandbox = new SnapshotSandbox();
        ((
            (window) = >{
                window.a = 1;
                window.b = 2;
                console.log(window.a,window.b);
                sandbox.inactive();
                console.log(window.a,window.b);
                sandbox.active();
                console.log(window.a,window.b);
            }

        ))(sandbox.proxy);
Copy the code

Personally suggested memory map

  • Update – Unchanged ↑ Update ↓ Restore
The snapshot Modify the state of The sandbox
active + ↑ (for update)
inactive + ↓ (represents reduction)

A link to the

Micro front end qiankun actual combat

Self-implementation of microfront-end singleSpa

This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign