Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Configuring a Vue 3 Host with qiankun for Multi-Framework Micro-Applications

Tech May 15 1

Host Shell: Vue 3.2+ / Vite 4+ Sub-Applications: Vue 3.2+, Angular 14+, React 18+

Host Application Configuration

Initialize standard Vue Router and navigation structure first. Within your primary layout component, define a dedicated rendering zone for child applications.

<template>
  <div class="shell-layout">
    <nav>
      <router-link to="/">Home</router-link>
      <router-link to="/sub/vue">Vue Module</router-link>
      <router-link to="/sub/ng">Angular Module</router-link>
      <router-link to="/sub/react">React Module</router-link>
    </nav>
    <hr />
    <main id="render-zone"></main>
  </div>
</template>

Proceed with installing the micro-frontend runtime:

npm install qiankun --save

Create an application registration file microAppsRegistry.ts:

import { registerMicroApps, start, LifeCycleFn } from 'qiankun';

type AppLifecycle = {
  beforeLoad?: LifeCycleFn;
  beforeMount?: LifeCycleFn;
  afterUnmount?: LifeCycleFn;
};

const appsToRegister = [
  {
    name: 'vue-sub-module',
    entry: '//localhost:8080',
    container: '#render-zone',
    activeRule: '/sub/vue',
    props: { theme: 'dark' }
  },
  {
    name: 'angular-sub-module',
    entry: '//localhost:4200',
    container: '#render-zone',
    activeRule: '/sub/ng',
    props: { lang: 'en' }
  },
  {
    name: 'react-sub-module',
    entry: '//localhost:3000',
    container: '#render-zone',
    activeRule: '/sub/react',
    props: { version: 'latest' }
  }
];

const lifecycleHooks: AppLifecycle = {
  beforeLoad: async () => console.log('Module loading initiated'),
  beforeMount: async () => console.log('Mount phase started'),
  afterUnmount: async () => console.log('Cleanup complete')
};

registerMicroApps(appsToRegister, lifecycleHooks);

start({
  sandbox: {
    strictStyleIsolation: true,
    experimentalStyleIsolation: true
  },
  prefetch: true
});

Import this registry into your Vue entry point src/main.ts:

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import './microAppsRegistry';

createApp(App).use(router).mount('#app');

Vue 3 Sub-Application Setup

Standard routing configuration remains unchanged. To enable qiankun compatibility within a Vite environment, integrate the dedicated adapter plugin:

npm install vite-plugin-qiankun --save-dev

Adjust the build configuration in vite.micro.config.ts:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import qiankunPlugin from 'vite-plugin-qiankun';

export default defineConfig({
  base: '/sub/vue/',
  plugins: [
    vue(),
    qiankunPlugin('vue-sub-module', { useDevMode: true })
  ],
  server: {
    host: '0.0.0.0',
    port: 8080,
    cors: true,
    origin: 'http://localhost:8080'
  }
});

Refactor the application bootstrap logic in src/index.ts to handle external hosting environments:

import { createApp } from 'vue';
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
import RootComponent from './App.vue';
import applicationRouter from './router';

let instanceCache: any = null;

if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  // Standalone execution mode
  const app = createApp(RootComponent);
  app.use(applicationRouter);
  app.mount('#app');
} else {
  renderWithQiankun({
    mount(props) {
      instanceCache = createApp(RootComponent);
      instanceCache.use(applicationRouter);
      instanceCache.mount(props.container?.querySelector('#app') || document.getElementById('app'));
    },
    unmount() {
      instanceCache?.unmount();
      instanceCache = null;
    },
    update(props) {
      console.log('Props updated:', props);
    }
  });
}

Angular Sub-Application Configuration

For Angular integration, the ecosystem relies on single-spa-angular. Initialize a fresh project with routing enabled:

ng new angular-micro-app --routing --prefix micro-ang
cd angular-micro-app
ng add single-spa-angular --project angular-micro-app

Modify main.single-spa.ts to inject the correct base href and lifecycle definitions:

import { APP_BASE_HREF } from '@angular/common';
import { singleSpaAngular, SingleSpaAngularOptions } from 'single-spa-angular';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

const extraProviders = [];

const angularLifecycles: SingleSpaAngularOptions<any> = {
  bootstrapFunction: (singleSpaProps) => {
    return platformBrowserDynamic(extraProviders)
      .bootstrapModule(AppModule)
      .catch(err => console.error(err));
  },
  template: '<micro-ang-root id="child-ng-instance"/>',
  Router: undefined,
  NgZone: undefined
};

export default singleSpaAngular(angularLifecycles);

Configure the routing module to dynamically set the base path based on the execution context. Ensure this matches the host's activeRule:

import { NgModule } from '@angular/core';
import { RouterModule, Routes, APP_BASE_HREF } from '@angular/router';

const routes: Routes = []; // Define your actual routes here

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [
    {
      provide: APP_BASE_HREF,
      useValue: window['__POWERED_BY_QIANKUN__'] ? '/sub/ng' : '/'
    }
  ]
})
export class AppRoutingModule {}

Update the root component selector and template wrapper to prevent DOM conflicts when multiple instances exist:

import { Component } from '@angular/core';

@Component({
  selector: '#child-ng-instance micro-ang-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {}

To allow independent local development while maintaining qiankun compatibility, modify the bootstrapping logic:

// Inside main.single-spa.ts or bootstrap file
import 'zone.js/dist/zone';

if (!(window as any).__POWERED_BY_QIANKUN__) {
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((err) => console.error(err));
}

If polyfills interfere with standalone runs, remove import 'zone.js' from polyfills.ts. Ensure the host application manages zone.js dependencies to avoid duplicate runtime injections.

React Sub-Application Considerations

React modules require the official @ice/stark or single-spa-react adapter. Configure entry points to export standard mounting/unmounting functions. Apply CSS scoping strategies and isolate global state to prevent framwork interference. Follow the same lifecycle pattern established in the Vue implementation.

Tags: qiankun

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.