In order to integrate new projects into the original system, we often use qiankun’s micro front-end solution to solve the integration demands.

Vue2 and VUe3 had little difference in actual use in Qiankun. There were some differences in main routing processing, which would also be pointed out in the paper and marked with [VUe3].

Through this article, you can learn how to integrate new and old projects using the micro front end in the VUE (2/3) technology stack. The main contents include:

  1. Different routing modes (history and hash), code transformation and deployment of master and sub-applications in development and production environments respectively;
  2. How to process the authentication logic before entering the sub-application;
  3. Master and child application style isolation processing;
  4. Communication between master and child applications;
  5. Load the microapplication under the application route;
  6. QA, summary of problems encountered during development.

Commonly used API

The following are two frequently used QIANkun apis, which have been linked and can be viewed on the official website:

RegisterMicroApps: The master application registers the microfront-end application

Start: Starts the micro-front-end application

1. Code transformation and deployment of master and sub-applications in development and production environments with different routing modes;

A. Modify the hash mode for both the primary and sub-application routes

Development environment, main application transformation:

import { registerMicroApps, start } from 'qiankun';
registerMicroApps(
  [
    {
      // The name of the micro front-end application
      name: 'app1'.// Micro front-end startup address
      entry: '//localhost:8100/'.// The micro front-end mounts the DOM
      container: '#app'.// The micro front end triggers routing
      activeRule: '#/app1'.// Static values passed from the master application to the child application
      props: {
        name: 'yuxiaoyu',},},],); start();Copy the code

Development environment, sub-application transformation:

// import file main.js
// The sub-application does not use the introduction of the qiankun, as long as it exposes the response of the declaration cycle hook to the main application
// Mount the instance
function render(props: any = {}) {
  const { container } = props;
  app = createApp(App);
  app.use(router);
  app.use(store);
  router.isReady().then(() = > {
    app.mount(container ? container.querySelector('#container') : '#container');
  });
}

__POWERED_BY_QIANKUN__ is mounted by the master application to the microapplication, which is used to determine the environment
// The following webpack injection publicPath method is provided. The development environment is used in this way, and the production is changed to vue.config.js, which will be described later.
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

__POWERED_BY_QIANKUN__=undefined direct render
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// The last three exposed methods are fixed: load render and destroy
export async function bootstrap() {}export async function mount(props: any) {
  render(props);
}
export async function unmount() {
  app.unmount();
  app._container.innerHTML = "";
  app = null;
  
  // The reason for reload: The microapp and the main app do not share navigation information in the project
  
      
// After uninstalling the microapp, it is reloaded to render the main app again. location.reload(); } Copy the code
/ / the router
const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

// Since the master app has an activeRule prefix when activating the child app, in hash mode, we need to add activeRule prefix to each level route. The master app is '#/app1', so we can add '/app1' before the child app.
export default[{path: '/app1/fujidaohang'.redirect: '/app1/fujidaohang/zijidaohang'.component: BothLayout,
    name: 'fujidaohang'.meta: {
      title: 'Parent navigation'.navPosition: 'top',},children: [{path: 'zijidaohang'.component: () = > import('@/apps/fujidaohang/views/zijidaohang.vue'),
        name: 'zijidaohang'.meta: {
          title: 'Sub-navigation'.navPosition: ' ',},},],}, {path: '/app1/course'.component: Course,
    name: 'course'.children: [],}];Copy the code
/ / the vue. Config. Js
const packageName = require('./package.json').name;

module.exports = {
  ...
  
  // Use the master application to identify sub-applications
  configureWebpack: {
    output: {
      library: 'app1'.libraryTarget: 'umd'.jsonpFunction: `webpackJsonp_${packageName}`,}}}Copy the code

Production construction deployment:

There are two ways to build deployment:

Deploy with the same or different domain name.

Same domain name deployment:

1. Consider packing subapplications into static paths to import static resources from the master application. This method applies to the following scenarios: The sub-applications do not need to be independently accessed and deployed. The advantage of this approach is that there is no o&M coordination to Nginx configuration, local packaging, and use the original deployment mode. Of course, it also loses the advantages of a micro front end, independent deployment of master and child.

2. There is another way to deploy packaged static resources on the same server and then add deployment and nginx-related configurations for the project. It can be loaded by domain name when the main application is accessed, or by relative path (since we are deployed on the same machine). Compared with the above scheme, this scheme increases the operation and maintenance cost, but retains the feature of independent release by the master.

Deployment without a domain name

The solution here is similar to 2 above, but the difference is that resources may be stored on different servers, so resources can only be loaded by domain name (that is, the entry configured in registerMicroApps needs to be a domain name, not a relative path, similar to the development environment). Remember to handle cross-domain.

Of course, you can also choose the corresponding deployment mode according to your needs. The official has given a detailed introduction: how to deploy.

Production construction, main application transformation:

// main.js
// Register the microapplication
registerMicroApps(
  [
    {
      name: 'app1'.// After the path is changed to deploy, the micro application will be stored in the main application directory, otherwise unchanged
      entry: '/static/index.html'.container: '#app'.activeRule: '#/app1'.props: {
        name: 'kuitos',},},],);Copy the code

Production construction, micro application transformation:

// main.js
// Remove the __webpack_public_path__ injection from qiankun
// if (window.__POWERED_BY_QIANKUN__) {
// __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
// }

// vue.config.js
module.exports = {
  ...
  
  // Add the package resource storage path corresponding to the main application above
  publicPath: '/static'
}
Copy the code

B. Transform the primary and sub-application routes into the history mode

Development mode:

Here we compare it to the Hash-dev pattern, listing only the differences.

/ / the main application
// main.js, registered microapplication has changed
registerMicroApps(
  [
    {
      name: 'app1'.entry: '//localhost:8101'.container: '#app'.// Change point: The active route changes from hash mode to history mode
      activeRule: '/app1'.props: {
        name: 'yuxiaoyu',},},],);// router.js 

const router = new VueRouter({

  // Change the hash mode to history
  mode: 'history'.base: process.env.BASE_URL,
  routes,
});
Copy the code
/ / application
// router/index.js
// [vue3] Since the sub-application is vue3 version, here is the difference between vue2 and vue3 in route processing. We just use the writing method of vue3 plus the required prefix app1
const router = createRouter({
  
  // Change to history mode and prefix it with activeRule
  history: createWebHistory('/app1'),
  routes,
});

// router.js

// Remove the app1 prefix here
export default[{path: '/fujidaohang'.redirect: '/fujidaohang/zijidaohang'.component: BothLayout,
    name: 'fujidaohang'.meta: {
      title: 'Parent navigation'.navPosition: 'top',},children: [{path: 'zijidaohang'.component: () = > import('@/apps/fujidaohang/views/zijidaohang.vue'),
        name: 'zijidaohang'.meta: {
          title: 'Sub-navigation'.navPosition: ' ',},},],}, {path: '/course'.component: Course,
    name: 'course'.children: [],}];Copy the code

Production construction deployment:

This is consistent with the hash mode change point, but only describes the differences.

Main application transformation:

// main.js
// Register the microapplication
registerMicroApps(
  [
    {
      // After the path is changed to deploy, the micro application will be stored in the main application directory, otherwise unchanged
      entry: '/static/index.html',}]);Copy the code

Micro application transformation:

// main.js
// Remove the __webpack_public_path__ injection from qiankun
// if (window.__POWERED_BY_QIANKUN__) {
// __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
// }

// vue.config.js
module.exports = {
  ...
  
  // Add the package resource storage path corresponding to the main application above
  publicPath: '/static'
}
Copy the code

2. How to deal with the authentication logic before entering the sub-application

When entering the micro-application and verifying whether the micro-application is logged in, we can consider authentication in the micro-application or the main application.

This section describes a scheme for intra-application authentication.

In qiankun, registerMicroApps provides a second parameter, lifeCycles- an optional, global microapplication lifecycle hook

RegisterMicroApps ([{...}], {// This uses authentication before entering the microfront end to ensure that you have logged in when entering the microfront end. beforeLoad: [ () => { if (!auth.check()) { vm.$router.push(loginPath); } }, ], },);Copy the code

3. Master and child apply style isolation

A. Antd is used for both main and sub-applications

There are official ways to change the antD prefix, such as changing ant to dida-ant, and how to ensure style isolation between the main application and the microapplication.

The ant-Design-vue documentation does not provide prefixCls parameters, but you can use the corresponding processing in the source code.

B. The custom style of the primary application conflicts with that of the child application

Here we can use the official provided start(options?) Put the microapplication into shadow DOM, which is supported by the browser.

start({
  sandbox: {
    // Master application & child application style isolation
    strictStyleIsolation: true.// Put it in shadow DOM}});Copy the code

Here we can see that the micro front end is put into shadow-root, which is where we can learn about the Shadow DOM.

After this isolation, we have some problems using external libraries like Ant Design, such as popup components, which were originally implemented in document.body, and we put the child application in shadow DOM, so we need to attach popUp as well. Ant Design officially provides a method to search for getPopupContainer;

4. Communication between master and child applications

A. Static value transfer

In Mounted and Render life cycles, we can get props data.

registerMicroApps(
  [
    {
      name: 'app1'.entry: '//localhost:8100'.container: '#app'.activeRule: '/app1'.props: {
        id: 1,},},],);Copy the code

B. Communication mechanism

The Qiankun official provided a definition method for creating master and sub-application communication. InitGlobalState (state), document link, after initialization returns three methods, onGlobalStateChange, setGlobalState, offGlobalStateChange, listen, set, and remove respectively.

/ / the main application
// Initialize state
// When initGlobalState is called, it is hung in props
const initialState = {
  userInfo: {}, // User information
};
const actions = initGlobalState(initialState);
actions.onGlobalStateChange((newState, oldState) = > {

  // newState: changed state; OldState Indicates the status before the change
  console.log('mainapp: global state changed', newState, oldState);
});
actions.setGlobalState({
  userInfo: {
    name: 'Zhangsan',}}); actions.offGlobalStateChange();Copy the code
export async function mount(props: any) {
  render(props);
  
  // props will inject the onGlobalStateChange, setGlobalState methods.
  console.log('props :>> ', props);

  app.config.globalProperties.$onGlobalStateChange =props.onGlobalStateChange;
  app.config.globalProperties.$setGlobalState = props.setGlobalState;
  props.onGlobalStateChange((newState: any, oldState: any) = > {

    // newState: changed state; OldState Indicates the status before the change
    console.log('microapp: global state changed', newState, oldState);
  });
  
  window.document.addEventListener('click'.() = > {
    props.setGlobalState({
      userInfo: {
        name: Math.random(),
      },
    });
  });
}
Copy the code

5. Load the microapplication under the application route

Background:

Vue3 as the main application, the original system as a child application (VUE2). So vue3(master) followed by vue2(child). Introduce subapplications to existing routes or components. As shown in figure:

The child applications need to be nested within the Layout component

Officials provide a way to access a route by applying it.

The official method is to access vue2 in the main application. So there’s a little bit of a difference here.

{
  path: '/huodongguanli'.redirect: '/trace_micro/project/projectList'.component: SideLayout,
  name: 'huodongguanli'.meta: {
    title: 'Event Management'.navPosition: 'side'.icon: () = > {
      returnh(ContactsOutlined); }},children: [{path: '/trace_micro/project/projectList'.// If the path does not need to be nested in the level 2 menu, write absolute path.
      component: { default: ' ' }, // Since we only declare a menu item here, we don't need a component (component content in the child application), we give an empty object with default
      name: 'lianluliebiao'.meta: {
        title: 'Link list'.navPosition: 'side',}}, {path: 'chuangjianjiangzuo'.// The route in the main application can be written normally
      name: 'chuangjianjiangzuo'.meta: {
        title: 'Creating a lecture'.navPosition: 'side',},},],},// Fuzzy matching is performed for routes that are not displayed in the main application menu
/ / 【 vue3 】
// Because in VUe3, Vue - router4. X [written cancelled the wildcard] (HTTP: / / https://next.router.vuejs.org/zh/guide/migration/index.html#%E5%88%A0%E9%99%A4%E4%BA%86-%EF%BC% 88%E6%98%9F%E6%A0%87%E6%88%96%E9%80%9A%E9%85%8D%E7%AC%A6%EF%BC%89%E8%B7%AF%E7%94%B1)
{
  path: '/trace_micro/:pathMatch(.*)*'.// [important] We use the notation provided in Router4.x to match the child application route
  hidden: true.component: SideLayout,
  name: 'projectOperate'.meta: {
    title: 'Create link'.navPosition: 'side',}}Copy the code

Other usage methods (start and mount nodes, etc.) are the same as those provided by the official.

6. Frequently asked Questions

The official FAQ documents are provided. If you encounter a problem, you can go here to check whether there is a corresponding matching solution.

Here are a few other bugs I encountered while accessing the project:

1. Share the same node with the active application#app. When you return to the master app, the master app is not displayed.

This is because when we initialized the micro front end, we replaced the contents of #app with the data of the child app, and when we returned, we did not remount it, so it can be easily handled on Reload.

2. Share the same node with the active application#app. The DOM appears to be mounted successfully, but the styles are not.

This is a different situation from the first one, mainly because we have the dom structure of the child application directly mounted to the main application, and the micro front end does not work. In mount life cycle, we can get props, mount a container inside this attribute, is the dom node mount micro front-end, can use props. Container. QuerySelector (‘ # app) to mount the application of vue.

3. The sub-application jumps to the main application.$router.pushDon’t take effect

The router used by our master and child applications is not an instance and the registered routes are different, so the child application cannot match the route of the master application. You can use history.push or location.href to jump.

4. When opening the master application and opening the child application, a message is reportedjsCould not load, or<Such as incorrect grammar

Generally, the path from the main application to the sub-application is incorrect. Check whether the path can lead to the sub-application resources.

5. Main application introductionbabel-polyfillSometimes, sometimesbabel-polyfillIntroduce the problem twice.

Let’s check to see if the child application also introduces babel-Polyfill separately. If it does, it will all be referenced in one place.

Most new applications are generated using vue-CLI, and babel-polyfill is not introduced separately. If this error is reported, check to see if there is a problem with the path of the child application and the resource is introduced incorrectly. The problem cited twice may also be due to the problem not cited by the sub-application. It has nothing to do with babel-Polyfill itself being introduced.

6. On style isolation

There are also scenarios that give style isolation, but the scenarios themselves have some problems. For example, the popup hanging on document.body issue we mentioned needs to be addressed. There may be some other issues to deal with.

The simplest and most effective way to do this is to use style scoped for component-level style isolation in both main and child applications. For using the same component library, different configurations can be resolved by modifying component and style prefixes.

The above is the actual development of micro front end (QIANkun) in VUE2 and 3. I hope it will be helpful to you.