Introduction: This paper mainly records the process of constructing qiankun project from 0 to 1 and some problems encountered in the process. It also provides a learning Demo for friends who want to contact Qiankun

1. Introduction and construction of project technology stack

The main application

   // 1. Create a project using vite and name it Dashboard
   npm init vite@latest dashboard -- --template vue
   // Because the project created using Vite is an empty project, we need to install the required libraries ourselves
   Install vuE-Router and vuex
   npm i vue-router -S
   npm i vuex -S
Copy the code

The routing configuration

// SRC create the router folder and index.js file
// index.js
import { createRouter, createWebHistory } from "vue-router";
import { BaseRoute, errorRouter } from "./baseRouter";
/** * Vite implements import of multiple modules from file system * docs: https://vitejs.cn/guide/features.html#glob-import * * the following code is equivalent to the webpack:  * const req = require.context('./modules',true,/\.js$/) * req.keys().forEach(path => Asyncroutes.push (req(path).default)) * * Reads js files in./modules folder */
let asyncRoutes = [];
const modules = import.meta.globEager("./modules/*.js");
for (const path in modules) {
  asyncRoutes.push(modules[path].default);
}
let routes = asyncRoutes.concat([...BaseRoute, ...errorRouter]);
// Create a route
const router = createRouter({
  history: createWebHistory(),
  routes: routes,
});    
export default router


// ./baseRouter.js
export const BaseRoute = [
  {
    path: "/".component: Layout
  },
];

export const errorRouter = [
  {
    path: "/error".component: Layout,
    children: [{path: "/ 403".name: "403".component: NotRights,
      },
      {
        /* * Match all routes *  * https://router.vuejs.org/guide/essentials/dynamic-matching.html#catch-all-404-not-found-route */
        path: "/:pathMatch(.*)*".name: "404".component: NotFound,
      },
    ],
  },
];

Copy the code

Vuex configuration

// SRC creates the store folder and index.js file
// index.js
import { createStore } from "vuex";

const modules = {};
const req = import.meta.globEager("./modules/*.js");
for (const path in req) {
  // Use the filename to represent the module name
  // /modules/user.js ==> user
  let pathName = path.split("modules/") [1].split(".") [0];
  if(! modules[pathName]) modules[pathName] = req[path].default; }const store = createStore({
   state() {
     return {};
   },
   modules,
});
export default store
Copy the code

The child application

// 1. Use Vue Cli to create a project and name it micro-user and micro-salary
vue create micro-user
vue create micro-salary
// Because the Vue Cli creates a project and automatically installs the Vue related libraries according to the requirements, there is no need to install the Vue related libraries
Copy the code

2. Qiankun configuration

First post official document address: Qiankun

The main application

// 1. Install qiankun
npm i qiankun -S

// 2. Register the microapplication through the registerMicroApps function
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
   {
       name: "micro-salary".// Mandatory. The name of the micro-application must be unique between micro-applications.
       entry: "//localhost:8001".// Mandatory. The entry of the micro application must be consistent with the address of the micro application
       container: "#view-main".// Mandatory, microapplication container node selector or Element instance.
       activeRule: "/micro-salary".// Mandatory, micro application activation rule
   },
   {
       name: "micro-user".entry: "//localhost:8002".container: "#view-main".activeRule: "/micro-user",}])// See the official document above for more parameters and how to use them

// 3. The main application route needs to recognize the micro application route
// router/baseRouter.js
export const BaseRoute = [
  {
    path: "/".redirect: "/system"}, {path: "/micro-salary/:morePath*".component: Layout
  },
  {
    path: "/micro-user/:morePath*".component: Layout
  },
];



// 4
start()
Copy the code

The child application

  1. Modify thevue.config.jsconfiguration
 const { defineConfig } = require("@vue/cli-service");
 const { name } = require("./package.json");
 const { resolve } = require("path");
 module.exports = defineConfig({
   / * key!!!!!! The basic path of the project package deployment must be written and consistent with the project running address, otherwise resources cannot be read */
   publicPath: "http://localhost:8002/".devServer: {
    port: "8001".// Need to be consistent with the main application registration entry
    headers: {
      "Access-Control-Allow-Origin": "*",}}})configureWebpack: {
    resolve: {
      alias: {
        "@": resolve(__dirname, "src"),}},output: {
      library: `${name}-[name]`.libraryTarget: "umd".// Package microapplications into umD library format
      /** * Webpack 5 will automatically infer a unique build name from package.json name and use it as the default for output.uniquename. * Since there is a unique name in package.json, output.jsonpFunction can be removed. * /
      // jsonpFunction: `webpackJsonp_${name}`,}},Copy the code
  1. Modifying Route Configurations
/* Because the qiankun was monitoring the route and filling in the activation rule when registering the micro-application to determine the rendered micro-application, the base path needed to be added to the route. The base path needed to match the activation rule of the micro-application */
const router = createRouter({
  history: createWebHistory(
    window.__POWERED_BY_QIANKUN__ ? "/micro-salary/" : "/"
  ),
  routes,
});
Copy the code
  1. Modify entry file (main.js)
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
let app = null;

function render(props = {}) {
  const { container } = props;
  app = createApp(App);
  app
    .use(store)
    .use(router)
    .mount(container ? container.querySelector("#app") : "#app");
}

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

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}
export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}
export async function unmount() {
  app.unmount();
}
Copy the code

The configuration of the microapplication is now complete

Fill in the project structure

// layout.vue
<template>
  <el-container>
    <el-header>
      <HeaderWrap />
    </el-header>
    <el-container class="layout-main">
      <el-aside class="nav-bar">
        <NavBar />
      </el-aside>
      <! -- Microapplication container -->
      <div id="view-main" />
      <! -- Master applies its own route rendering -->
      <router-view />
      <! <div id="view-main"> <router-view/> </div>
    </el-container>
  </el-container>
</template>

// NavBar.vue
<template>
  <el-menu
    active-text-color="#ffd04b"
    background-color="#545c64"
    class="el-menu-vertical-demo"
    :default-active="activeMenu"
    text-color="#fff"
    router
    @select="menuSelect"
  >
    <el-menu-item index="/micro-user/">
      <span>User microapplications</span>
    </el-menu-item>
    <el-menu-item index="/micro-salary/">
      <span>Salary microapplication</span>
    </el-menu-item>
  </el-menu>
</template>
Copy the code

The project is set up. See the preview below

3. Sign up for microapplication optimization

What we did above is load some fixed microapplications, but what if the number of microapplications is not fixed? For example, there are only two microapps, payroll and user, and after a while, we add a system Settings microapp. Do we need to modify the existing code of the main app?

The following solutions belong to the author’s idea:

Because we know that when registering a microapplication, we only need to fix some configuration, such as: Name, entry, container, etc. In this way, we can store all micro-application configuration items in the database, insert a piece of data into the database every time there is a new micro-application, and then obtain all micro-applications through the interface to complete batch registration

Here is the implementation of the front end:

// 1. Create a micro module in the main app Store
import { router } from "@/router";
import Layout from "@/layout/index.vue";
import { prefetchApps, registerMicroApps, start } from "qiankun";

export default {
  namespaced: true.state() {
    return {
      isLoadMicro: false.// Check whether the micro application route is loaded
    };
  },
  mutations: {
    changeLoadStatus(state, payload) {
      state.isLoadMicro = payload;
    },
    startMicroApps(state, payload) {
      // Register the microapplication
      registerMicroApps(payload);
      // Start microservices
      start({ sandbox: { strictStyleIsolation: true}}); }},actions: {
    // Load the micro-application route
    loadMicroRouter({ commit }) {
      return new Promise((res) = > {
        // Simulate an asynchronous request
        setTimeout(() = > {
          const microRouter = [
            {
              name: "micro-salary".entry: "//localhost:8001".container: "#view-main".activeRule: "/micro-salary"}, {name: "micro-user".entry: "//localhost:8002".container: "#view-main".activeRule: "/micro-user",},]; microRouter.forEach((micro) = > {
            /* Add a route to each micro app to prevent the rendering of 404 pages by addRoute. Routes added by addRoute do not take effect immediately, they need to trigger a new navigation so you need to change the route blocking code */
            router.addRoute({
              path: `${micro.activeRule}/:morePath*`.component: Layout,
            });
          });
          
          commit("changeLoadStatus".true);
          commit("startMicroApps", microRouter);
          res();
        }, 1000); }); ,}}};Copy the code
import { store } from "@/store";
router.beforeEach(async (to) => {
    if(! store.state.micro.isLoadMicro) {await store.dispatch("micro/loadMicroRouter");
      /** * return {... To, replace:true} * Because the route is added dynamically, no route is matched for the first time, route 404 will be matched * and when path and name are available, the redirect path is name *, so only the redirect path can be used as the basis */
      return { path: to.path, replace: true };
    } else {
      return true; }});Copy the code

The project only used the most basic two apis provided by Qiankun. For more apis, you need to check the official website

At this point, the project is complete. Thanks for reading