Remember before

Suitable for vUE stack developers who are doing front-end engineering

background

When 20+ front-end developers spend a year iterating together to develop a front-end VUe2.0 project, there is no doubt that the result will be a giant front-end project like this

Each subroutine is related to each other, reference.

This leads to a number of problems:

  1. Slow project start
  2. Hot update slow
  3. Development, large memory footprint, stack burst probability increased
  4. Builds are slow and deployment environments (development, tuning, testing) take longer
  5. Only the full front-end package can be built, and whether the test has been verified needs to be re-verified
  6. Deployment of a single artifact (a single functional subsystem) is not satisfied
  7. .

explain

Here,
item \ color {red} {item}
It refers to: independent subsystem + corresponding subsystem independent background (in the customer side deployment may not be kicked out of the soul of the interrogation: “I only want the function of system A, why do you also deploy B, C? ).

The pre

The main factors leading to the above series of problems are large project size and serious coupling. The solution to the problem is —- reduce the size of the project \color{red}{reduce the size of the project} reduce the size of the project, that is, divide the “big MAC” into a number of different flavor “small meat pies”.

Advantages and disadvantages of iframe and single-SPA

Based on the experience of other developers and personal practice, the comparison results of the two methods are as follows:

Technical solution advantages disadvantages
The iframe solution 1, CSS, JS completely isolated, each micro front end will never affect each other 1. Long loading time; 2. You may have to retrieve resources every time to load the latest child application. 3. The integrated scrollbar problem exists; 4. The bullet frame problem
Single – spa package 1, there is a ready-made package library, can be directly used; 2, can achieve seamless page switching; 3. Load on demand 1. Global pollution

After comparison, the use of single-SPa-based universe frame \color{red}{universe frame} universe frame was finally decided.

Universe framework: It is a micro-front-end implementation library based on single-SPA under Ali and the protocol type is MIT.

StateDiagram -v2 User --> Master: access /master Master --> childApp1: load master --> childApp2: Load master -->...... Master --> childAppn: load

The implementation of

Our big MAC environment depends on:

  1. Vue buckets
  2. Element, Layer (historical legacy)
  3. Echarts, jsplumb, G6

Our Big MAC layout looks like this:

So it’s a nice division, navigation and menus for the master app, content for the childApp.

Refining master application

  1. Remove the content area clean, and can start normally.
  2. Install the universe framenpm i qiankun -S
  3. Register childApp in main.js
// main.js
import { registerMicroApps } from "qiankun";
const CHILDAPPCONFIG = [
 {
    name: 'child-app-name'.// The child application name is unique
    entry: 'localhost:7100/'./ / the entry
    container: '#yourContainer'./ / childApp container
    activeRule: '/yourActiveRule2'.// Activate the rule}]; registerMicroApps(CHILDAPPCONFIG);Copy the code
  1. To start things around
// Start the universe in the place corresponding to the chilApp container
<div id="yourContainer"></div>

import { start } from "qiankun";

mounted(){
  start();
}

// Better solution -- Use window variables to avoid repeated launches
mounted(){
    if(!window.qiankunStarted){
        window.qiankunStarted = true; start(); }}Copy the code

Adjust the childApp app

  1. Add the hook functions needed for master in main.js
// main.js
let instance = null;
let routes = null;
function render(props = {}) {
  const { container } = props;
  routes = router;
  instance = new Vue({
    router: routes,
    store,
    i18n,
    data: {
      Bus: new Vue()
    },
    render: (h) = > h(App),
  }).$mount(container ? container.querySelector('#app') : '#app');
  Vue.prototype.modal.initContext(instance);
  Vue.prototype.customData.initContext(instance);
  window._vm = instance;
}

// Independent runtime
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// The hook that the master needs
export async function bootstrap() {
  console.log('Child application starts loading... ');
}
export async function mount(props) {
  render(props);
  return new Error("Sorry, there was a problem with the response."); // This return is used to handle a timeout blank screen
}
export async function unmount() {
  if (instance) {
    instance.$destroy();
    instance.$el.innerHTML = ' ';
    instance = null;
    routes = null; }}export async function update(props) {
  renderPatch(props);
}
Copy the code
  1. Modify the configuration in vue.config.js
// vue.config.js

const name = "child-app-name"; // Align with name when main registerMicroApps is applied
module.exports = {
    publicPath:"/childApp1/".// Deploy the same nginx as master
    devServer: {headers: {'Access-Control-Allow-Origin'The '*'.// Configure the local development environment to be used by the master}},configureWebpack: {// Build the environment build output configuration
        output: {library: `${name}-[name]`.libraryTarget'umd'.jsonpFunction`webpackJsonp_${name}`,}}}Copy the code

The results of

PS: In the preceding implementation, the common basic configuration is used, and the sub-application is strongly related to the routing address. In the actual development and use process, it is highly likely that the master app directly uses loadMicroApp and delivers the content area identifier to childApp, and then childApp renders the content area at last.

Master adjusts the principal logic

StateDiagram -v2 User actions --> Master --> Render: ChildApp master --> childApp1:loadMicroApp --> childApp master --> childApp1:loadMicroApp --> childApp1: ChildApp1 --> render: Master --> childApp2. ChildApp2 --> Render master -->...... . -- > rendering

Active loading: the way in which the master app actively pulls static childApp resources and renders them into the specified dom by handling the unique identifier (usually a routing address) passed in by the user, matching the childApp activation rules, and working with nginx.

Master code adjustment

  1. The original start method is removed and loaded using loadMicroApp
this.microAppInstance = loadMicroApp({
    name:"childApp1",
    entry,
    container,
});
Copy the code

Need to destroy childApp old instance, enclosing microAppInstance. Unmount to ().

Adjustments in childApp

  1. Added content area identification entry
// componentIdList
export default {
    "10000001":() = >import("10000001.vue"); / / page level
    "10000002":() = >import("10000002.vue"); . }Copy the code
  1. Dynamic components load content areas
// template
<component :is="componentID"></component>

// computed
componentID(){
    return componentIdList[ID];
}
Copy the code
  1. ComponentIdList Content area identity entry
  2. The ID is the content area identifier passed by the master to the child application, which can be a routing address, passed through initGlobalState

conclusion

  • The childApp container DOM must already exist when the start method is called.
  • When childApp builds, publicPath is written as a relative path.
    • Cookie and master shared
    • If you need to change the publicPath, you can directly change it in nginx without rebuilding the master. Nginx configuration is as follows:
    /childApp1 { root /childApp1; # you can use proxy_pass to point to other nginx index index.html; }Copy the code

The problem back

【 答 案 1】 Lifecycle function’s promise did not resolve or reject

  • Cause: Mount does not return a value
  • Return new Error(” Sorry, there was a problem with the response “) from the mount of the main.js child application;

[Question 2] The layer level starts at 100000

  • Cause: Layer repeated declaration
  • Solution: Start sandbox; Or set window.layer=null before loading the child application

Error: [qiankun] You need to export lifecycle functions in childApp1 entry

  • Cause: childApp1 starts repeatedly
    • Processing: call before loadMicroApp await this. MicroAppInstance. Unmount to (), destruction of the old.

TypeError: Failed to execute ‘initMouseEvent’ on ‘MouseEvent’: parameter 4 is not of type ‘Window’

  • Cause: After proxy Window is applied, the window type is changed
  • Action: Set sandbox:false

To be continued……….