qiankun - Documentation

What is Qiankun?

Qiankun (乾坤, meaning “Heaven and Earth” in Chinese) is a powerful and versatile micro-frontend solution built on top of Single-SPA. It simplifies the development and integration of multiple independent applications (micro-frontends) into a single, cohesive user experience. Unlike many other micro-frontend approaches, Qiankun emphasizes ease of use and seamless integration with existing applications, allowing you to gradually adopt a micro-frontend architecture without requiring extensive refactoring. It provides a standardized API to handle the lifecycle of your sub-applications, managing their loading, mounting, and unmounting within the main application (the “container” application). This allows developers to work on individual applications independently and deploy them autonomously, fostering faster iteration cycles and improved team collaboration.

Key Concepts and Terminology

Benefits of Using Qiankun

Comparison with Other Microfrontend Solutions

Qiankun distinguishes itself from other micro-frontend solutions by its simplicity, ease of use, and strong emphasis on integration with existing projects. While other solutions might offer more advanced features or focus on specific architectural patterns, Qiankun prioritizes developer experience and a low barrier to entry. This makes it particularly well-suited for teams looking to quickly adopt a micro-frontend strategy without significant upfront investment or architectural overhaul. Compared to solutions requiring extensive configuration or custom infrastructure, Qiankun offers a streamlined approach with minimal overhead, facilitating faster development and easier maintenance. The default sandbox also provides a relatively simple but effective way to isolate sub-applications from each other.

Setting up a Qiankun Project

Project Initialization

Before starting, ensure you have Node.js and npm (or yarn) installed. We’ll assume you’re using a create-react-app based main application for this example, but Qiankun supports various frameworks. You can adapt these instructions to your preferred setup. Begin by creating a new React application using:

npx create-react-app my-qiankun-app
cd my-qiankun-app

This command creates the base structure for your main (container) application. You can name it differently if you prefer. We will refer to this directory as my-qiankun-app throughout this section. You will also need to create separate projects for each of your sub-applications, using your framework of choice (e.g., another create-react-app, a Vue project, etc.).

Installing Necessary Packages

Navigate to your my-qiankun-app directory and install Qiankun:

npm install @qiankun/react @qiankun/runtime

or if using yarn:

yarn add @qiankun/react @qiankun/runtime

@qiankun/react provides React-specific integration helpers, while @qiankun/runtime contains the core Qiankun functionalities. You will install framework-specific packages in your sub-applications (e.g., @qiankun/vue for Vue sub-applications).

Configuring the Main Application

  1. Import and Register Sub-applications: In your main application’s entry point (usually src/index.js or src/App.js), import registerMicroApps from @qiankun/react and configure it to register your sub-applications. This involves specifying the sub-application’s name, entry URL (the remoteEntry URL), and active rule (used to determine when to mount the sub-application). Example:
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  {
    name: 'sub-app-1',
    entry: '//localhost:8080', // Replace with your sub-app's URL
    container: '#sub-app-container', // Replace with a div ID in your main app
    activeRule: '/sub-app-1',
  },
  {
    name: 'sub-app-2',
    entry: '//localhost:8081', // Replace with your sub-app's URL
    container: '#sub-app-container',
    activeRule: '/sub-app-2',
  },
]);

start();
  1. Create Container Element: Add a <div> element with the ID specified in the container property in your main application’s component tree. This div will act as a placeholder for your sub-applications.

Creating Sub-applications

Create separate projects for each sub-application using your preferred framework. For example, if using React, you would create another create-react-app project. Then, install the corresponding Qiankun package (e.g., @qiankun/react). In your sub-application’s entry point, export the necessary bootstrap, mount, and unmount functions as required by Qiankun’s runtime. A basic example (using React):

// sub-app/src/index.js
import { render } from 'react-dom';
import App from './App';
import { createRoot } from 'react-dom/client';

export async function bootstrap() {
  console.log('react app bootstrapped');
}

export async function mount(props) {
  console.log('props from main framework', props);
  const root = createRoot(props.container);
  render(<App {...props} />, root);
}

export async function unmount(props) {
  console.log('unmount');
  ReactDOM.unmountComponentAtNode(props.container);
}

This exposes the required functions to the container application. Remember to configure your build process to generate a remoteEntry.js file (or a similar file) which acts as your sub-application’s entry point. The location of this file should be reflected in the entry field of your main application’s configuration.

Running the Application

Start your sub-applications independently (e.g., npm start within each sub-application directory). Then, start your main application using npm start in the my-qiankun-app directory. Navigate to different routes (defined in the activeRule) to load and interact with the corresponding sub-application. Remember to replace placeholder URLs with the actual URLs where your sub-applications are running. These are typically development server URLs while developing. In production, these would be your deployment URLs.

Developing Sub-applications

Creating a Sub-application

Creating a sub-application involves setting up a new project using your preferred framework (React, Vue, Angular, etc.). The process largely depends on the chosen framework; however, the key is to structure the project to export the necessary lifecycle functions that Qiankun expects. These functions are bootstrap, mount, and unmount. This export is usually handled through a dedicated entry point file (often named index.html or similar for simpler applications, or a Javascript entry point generated as part of the build process for larger applications).

The sub-application should be developed independently and built as a separate entity. The build process should generate a specific output file (usually named remoteEntry.js although this is configurable) that exports these lifecycle functions. This remoteEntry.js file contains the necessary information for Qiankun to load and manage the sub-application within the main application.

Ensure that your build configuration includes a proper output path and filename so the main application can correctly load the remoteEntry.js file.

Registering Sub-applications

Sub-applications are registered within the main application using the registerMicroApps function from @qiankun/react (or the equivalent function for other frameworks). The registration process involves providing an object for each sub-application. This object must contain at least the following properties:

Example:

registerMicroApps([
  {
    name: 'sub-app-one',
    entry: 'http://localhost:8080/remoteEntry.js', // Adjust for your specific build output
    container: '#subapp-container',
    activeRule: '/sub-app-one',
  },
  // ... more sub-applications
]);

Lifecycle Hooks

Qiankun provides crucial lifecycle hooks for each sub-application:

Proper implementation of these hooks is vital for ensuring seamless integration and a smooth user experience.

Communication Between Sub-applications and Main Application

Communication between sub-applications and the main application can be achieved through several mechanisms:

Data Sharing and Communication

For more complex scenarios, a more structured approach might be necessary. Consider using a dedicated communication mechanism like a message bus, a centralized state management solution, or a dedicated API layer. The chosen method should balance efficiency and maintainability, while keeping in mind the independence of sub-applications. Over-reliance on global state can lead to tightly-coupled and difficult-to-maintain systems.

Handling Sub-application Errors

Implement proper error handling within each sub-application and within the main application’s integration logic. The main application should gracefully handle errors that occur during sub-application loading, mounting, or unmounting. This may involve displaying informative error messages to the user or logging detailed error reports for debugging purposes. Use try...catch blocks appropriately within lifecycle hooks and other error-prone parts of the code. Consider centralized error reporting mechanisms to aggregate errors from various sub-applications.

Advanced Configuration

Customizing the Qiankun Sandbox

Qiankun’s default sandbox provides basic isolation, but for more stringent requirements or specific needs, you can customize the sandbox behavior. Qiankun allows you to provide a custom sandbox implementation. This is particularly useful when dealing with conflicting libraries or global variables between sub-applications. A custom sandbox function receives the sub-application’s entry point (entry) and the container element (container) as parameters and returns an object containing the proxy and patch functions. These functions allow you to intercept and control access to global variables and the DOM. Refer to the Qiankun documentation for detailed examples and instructions on how to implement a custom sandbox. Careful consideration should be given to performance implications when implementing a custom sandbox, as overzealous sandboxing can lead to performance bottlenecks.

Asynchronous Loading of Sub-applications

By default, Qiankun loads sub-applications synchronously, meaning the main application waits for the sub-application to load before proceeding. For improved performance, especially in scenarios with many sub-applications or large sub-applications, you can implement asynchronous loading. This involves optimizing the network requests for the remoteEntry.js files and potentially using techniques like code-splitting or pre-fetching to pre-load resources. However, be mindful of the added complexity asynchronous loading introduces to the overall architecture.

Using Different Frameworks for Sub-applications

One of Qiankun’s significant strengths is its framework-agnostic nature. You can easily integrate sub-applications built with different frameworks (React, Vue, Angular, etc.) within a single main application. The key is to ensure that each sub-application exports the correct lifecycle methods (bootstrap, mount, unmount) as expected by Qiankun’s runtime. You might need to install framework-specific Qiankun packages (e.g., @qiankun/vue for Vue sub-applications) to ensure smooth integration.

Pre-loading Sub-applications

Pre-loading sub-applications can significantly improve perceived performance by loading resources before they’re actually needed. This can be implemented by fetching the necessary assets (e.g., JavaScript bundles) in the background while the user is interacting with other parts of the application. Qiankun doesn’t directly handle pre-loading, but you can implement this using browser APIs like fetch or other techniques such as resource hints (<link rel="preload">). Strategic implementation of pre-loading requires careful consideration to avoid excessive resource consumption and negatively impacting the user experience.

Implementing Custom Error Handling

While Qiankun provides basic error handling, you can enhance it by implementing custom error-handling mechanisms. This may involve creating custom error boundaries within the main application or individual sub-applications to catch and handle exceptions gracefully. For instance, you could display informative error messages to the user, log errors to a centralized logging service, or implement fallback mechanisms. Comprehensive error handling is crucial for building robust and reliable micro-frontend applications.

Performance Optimization Strategies

Performance optimization is crucial for large micro-frontend applications. Strategies include:

Deployment and Scaling

Deploying the Main Application

Deploying the main application involves standard deployment procedures for your chosen framework and hosting platform. This typically involves building the application (often creating a production-optimized build) and deploying the resulting artifacts (HTML, CSS, JavaScript, and assets) to a web server. Ensure the server is configured correctly to serve the necessary files and handle routing to the various sub-applications based on the activeRule configuration within your main application. The deployment process might involve using CI/CD pipelines to automate the build and deployment steps. Consider using a reverse proxy or load balancer to manage incoming requests and distribute traffic effectively, especially as your application grows.

Deploying Sub-applications

Sub-applications are deployed independently, similar to the main application. Each sub-application has its own build process and deployment pipeline. The key difference is that the deployment target for each sub-application only needs to expose the remoteEntry.js file (or equivalent). This file serves as the entry point for Qiankun to load and mount the sub-application. The URL of this remoteEntry.js file needs to be accurately specified in the main application’s configuration (registerMicroApps). Deploying sub-applications independently allows for faster iteration cycles and parallel deployments.

Scaling Strategies

Scaling a micro-frontend application built with Qiankun involves scaling both the main application and the individual sub-applications. Strategies include:

The optimal scaling strategy depends on the specific requirements of your application, the anticipated load, and your budget. Careful monitoring and performance analysis are crucial for making informed scaling decisions.

Monitoring and Logging

Implementing robust monitoring and logging is essential for maintaining and scaling a micro-frontend application. Consider using dedicated monitoring tools to track key metrics such as:

Centralized logging and monitoring systems allow you to gain a holistic view of your application’s health and performance. This enables proactive identification and resolution of issues, ensuring a high level of availability and a positive user experience.

Best Practices and Troubleshooting

Code Organization and Maintainability

Maintaining a clean and well-organized codebase is crucial for the long-term success of any project, especially a micro-frontend application. For Qiankun projects, consider these best practices:

Testing Strategies

A robust testing strategy is essential for ensuring the quality and reliability of your Qiankun application. Consider incorporating these testing approaches:

Common Issues and Solutions

Some common issues encountered while working with Qiankun include:

Consult the Qiankun documentation and online resources for detailed solutions to specific issues.

Security Considerations

Security is paramount in any software project. When using Qiankun, keep these considerations in mind:

API Reference

This section provides a concise overview of the core Qiankun APIs. For detailed information and examples, refer to the official Qiankun documentation. Note that the exact API might change slightly depending on the Qiankun version.

registerMicroApps

This function registers the micro-applications (sub-applications) with Qiankun. It takes an array of application configurations as input and returns nothing. Each configuration object must include at least the following properties:

Example:

registerMicroApps([
  {
    name: 'app1',
    entry: '//localhost:8000',
    container: '#container',
    activeRule: '/app1',
  },
]);

start

This function starts Qiankun’s lifecycle management. It begins monitoring the URL and updates the active micro-applications accordingly. It takes an optional configuration object as a parameter. This allows for options such as setting the prefetch (whether to prefetch micro-applications), and sandbox settings. Calling start() is crucial after registering micro-apps via registerMicroApps().

Example:

start(); // Starts Qiankun

loadMicroApp

This function loads a specific micro-application asynchronously. It takes a configuration object similar to registerMicroApps, but only needs to specify a single sub-application. It returns a promise that resolves when the micro-application has successfully loaded and is ready to mount. Useful when you want to load a sub-application dynamically based on some other event outside of the initial routing.

Example:

loadMicroApp({
    name: 'app1',
    entry: '//localhost:8000',
    container: '#container',
}).then(() => console.log('app1 loaded'))

unmountRootParcel

This function unmounts the specified micro-application. This removes the sub-application from the DOM and performs the sub-application’s unmount lifecycle method. Takes the name of the application as an argument. It’s important for cleaning up resources.

Example:

unmountRootParcel('app1');

sandbox (Custom Sandbox)

While not a standalone function, the sandbox option within the start() function or within individual micro-application registrations lets you customize Qiankun’s isolation mechanism. By default, Qiankun uses a proxy-based sandbox. You can provide a custom sandbox implementation to fine-tune the isolation level and control how the micro-application interacts with the global environment of the main application. This requires a detailed understanding of Qiankun’s internal mechanisms and is generally needed only in advanced scenarios.

qiankun.initGlobalState

This function initializes a global state instance. This state can then be used for communication and data sharing between the main application and sub-applications. It takes an initial state object as an argument and returns a global state instance. The instance provides methods to access and update the shared state. The global state is typically used to transmit data and update application settings.

Example:

const globalState = qiankun.initGlobalState({ count: 0 });

qiankun.getGlobalState

This function retrieves the global state instance initialized by qiankun.initGlobalState. This provides a way for any part of the micro-frontend application (both main application and sub-applications) to access the global state and retrieve information.

Example:

const globalState = qiankun.getGlobalState();

Remember that while these functions provide a simplified overview, referring to the official Qiankun documentation is crucial for more detailed information, including advanced options and error handling.

Examples and Use Cases

Simple Example: Two Sub-applications

This example demonstrates a simple setup with two sub-applications. Assume you have two React applications: app1 and app2. Each is a standard Create React App project, but they have been modified to export the Qiankun lifecycle methods (bootstrap, mount, unmount). They also have been built to output a remoteEntry.js file containing this export.

Main Application (index.js):

import { registerMicroApps, start } from 'qiankun';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);


registerMicroApps([
  {
    name: 'app1',
    entry: '//localhost:8080/remoteEntry.js',  // Replace with your app1 URL
    container: '#subapp',
    activeRule: '/app1',
  },
  {
    name: 'app2',
    entry: '//localhost:8081/remoteEntry.js',  // Replace with your app2 URL
    container: '#subapp',
    activeRule: '/app2',
  },
]);

start();

reportWebVitals();

Main Application (App.js):

import React from 'react';
import { Link } from 'react-router-dom';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';


function App() {
  return (
    <Router>
      <div>
        <nav>
          <Link to="/app1">App 1</Link>{' '}
          <Link to="/app2">App 2</Link>
        </nav>
        <div id="subapp"></div>
      </div>
    </Router>
  );
}

export default App;

Remember that you need a <div id="subapp"></div> in your main application’s HTML. This is where the sub-applications will be rendered. App1 and App2 need to have their own routing configurations within the remoteEntry.js build output.

Complex Example: Multiple Sub-applications and Data Sharing

For more complex scenarios with numerous sub-applications and data sharing, consider using a centralized state management solution like Redux or Zustand. The global state provided by Qiankun (qiankun.initGlobalState and qiankun.getGlobalState) can also be a solution, but for larger applications, a more robust solution may be needed to avoid unexpected behavior. Sub-applications can access and update this shared state, enabling communication and data exchange. The main application might also act as a central hub, coordinating data flow between sub-applications.

Real-world Use Cases

Qiankun is suitable for various real-world scenarios:

Integrating with Other Libraries

Qiankun can be integrated with various libraries to enhance functionality:

The choice of libraries depends on the specific requirements and preferences of your project. Ensure compatibility between the libraries and Qiankun.