Integrating a React Micro‑Frontend into a Vue‑Based DSP Platform Using qiankun
The Cloud Music DSP team unified its Vue contract platform and React bidding platform by embedding the React app as a qiankun micro‑frontend, replacing iframe and MPA approaches, configuring HTML entry, lifecycle hooks, routing and CSS isolation, and addressing routing, style, and CORS challenges for seamless integration.
Business Background
The Cloud Music advertising DSP (Demand‑Side Platform) consists of two separate systems: a contract platform built with Vue and a bidding platform built with React. Because the frameworks were chosen historically, they are not unified. A new requirement demands that the same module be added to both platforms, and similar requests are expected in the future. To reduce maintenance cost and enable component reuse, the team explored ways to unify the tech stack by embedding the React system into the Vue project.
Why Not Use an iframe?
Browser history stack issues : Navigating back in an iframe can require many steps, leading to a poor user experience.
Application communication : Cross‑origin iframes prevent direct access to URL parameters; developers usually resort to postMessage.
Cache problems : After an iframe‑based app is updated, browsers may still serve cached content, requiring timestamp busting or forced refresh.
MPA + Routing Distribution
Using Nginx (or similar) to route requests to different business applications works, but it forces each application to replicate the same UI layout (sidebar, header, content area). The approach has the following pros and cons:
Advantages
Supports multiple frameworks.
Each application can be deployed and run independently.
Complete isolation between applications.
Disadvantages
Longer load time for each isolated app, resulting in a poor experience.
Common UI parts (navigation, header) need large‑scale changes, reducing reusability.
Micro‑Frontend Options
Base mode : A routing‑based approach where a base application listens to route changes and loads different sub‑applications, achieving decoupling.
EMP (Webpack 5 Module Federation) : A decentralized micro‑frontend solution that enables resource sharing and communication while keeping applications isolated.
Overall, iframes are suitable only for simple, low‑performance third‑party systems. MPA cannot satisfy both cost and experience requirements. Both the base mode and EMP are good choices, and because qiankun is widely used and mature, the team finally selected qiankun.
qiankun
qiankun(pronounced “乾坤”) is a front‑end micro‑service framework released by Ant Financial, built on top of single‑spa. It follows a routing‑distribution model. Unlike the original single‑spa which uses a JS entry to load sub‑applications, qiankun adopts an HTML entry, which simplifies resource fetching and insertion.
Limitations of JS Entry :
Only one JS entry file is allowed.
Static assets (images, CSS) must be bundled into the JS file.
Code splitting cannot be applied.
With an HTML entry, qiankun fetches the HTML document, extracts JS and CSS resources, and injects them into the designated container automatically.
Implementation Practice
The existing Vue project serves as the base application. The new feature is built with Create React App as a micro‑frontend. The following steps illustrate the integration.
Base Application Refactoring
The base (main) app is generated with vue‑cli. Its original structure and logic remain unchanged, but a dedicated container #frame is added to host the React sub‑application.
First, create a directory micro to hold all micro‑frontend related code and initialize qiankun globally.
Route configuration file app.js
// 路由配置
const apps = [
{
name: 'ReactMicroApp',
entry: '//localhost:10100',
container: '#frame',
activeRule: '/react'
}
];Application registration function
import { registerMicroApps, start } from "qiankun";
import apps from "./apps";
// Register sub‑applications (wrapped for future parameter injection)
export const registerApp = () => registerMicroApps(apps);
// Export qiankun start function
export default start;Layout component (Vue)
<section class="app-main">
<transition v-show="$route.name" name="fade-transform" mode="out-in">
<!-- Main app rendering area -->
<router-view />
</transition>
<!-- Sub‑app rendering area -->
<div id="frame" />
</section>In the main component, import and invoke the registration and start functions:
import startQiankun, { registerApp } from "../../../micro";
export default {
name: "AppMain",
mounted() {
// Initialize configuration
registerApp();
startQiankun();
},
};The two essential qiankun APIs used are registerMicroApps and start.
Note: Initialization is placed in the mounted lifecycle to guarantee that the container element exists.
Creating the qiankun Sub‑Application (React)
Using Create React App, the sub‑application must expose a set of lifecycle functions for qiankun to call.
public‑path.js (placed outside src) ensures that static assets resolve correctly when the app is loaded as a micro‑frontend:
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}Router base configuration – the base name is set to /react only when running under qiankun:
const BASE_NAME = window.__POWERED_BY_QIANKUN__ ? "/react" : "";
...<br/><Router base={BASE_NAME}>
...<br/></Router>Lifecycle functions (index.js) :
/**
* bootstrap is called once when the micro‑app is initialized.
*/
export async function bootstrap() {
console.log("bootstraped");
}
/**
* mount is called each time the app enters.
*/
export async function mount(props) {
console.log("mount", props);
render(props);
}
/**
* unmount is called each time the app leaves.
*/
export async function unmount() {
console.log("unmount");
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
}All lifecycle functions must return a Promise.
Webpack configuration adjustments to expose the app as a UMD library and enable cross‑origin requests:
module.exports = {
webpack: (config) => {
// Library name must match the registration name
config.output.library = `ReactMicroApp`;
// Expose as UMD for all module systems
config.output.libraryTarget = "umd";
// Set jsonp function name for on‑demand loading
config.output.jsonpFunction = `webpackJsonp_ReactMicroApp`;
config.resolve.alias = {
...config.resolve.alias,
"@": path.resolve(__dirname, "src"),
};
return config;
},
devServer: function (configFunction) {
return function (proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
// Disable host check for fetch access
config.disableHostCheck = true;
// Enable CORS
config.headers = { "Access-Control-Allow-Origin": "*" };
// Enable history fallback for SPA routing
config.historyApiFallback = true;
return config;
};
},
};These changes expose the lifecycle functions to the host and allow cross‑origin resource fetching.
In the host router, define a route that loads the React micro‑app:
{
path: '/xxx',
component: Layout,
children: [
{
path: '/xxx',
component: () => import('@/micro/app/react'),
meta: { title: 'xxx', icon: 'user' }
}
]
},Issues Encountered During Integration
Sub‑application fails to load : Check console for activation rule errors or missing container #frame during start.
Base app routing mode : The base app uses hash routing; sub‑apps must use the same mode, otherwise route changes break activation.
CSS style conflicts : By default qiankun does not sandbox CSS. Enable strict style isolation with { strictStyleIsolation: true } to wrap the sub‑app in a Shadow DOM.
Other notes
Older jQuery‑based multi‑page apps are harder to migrate; iframe may still be preferable for them.
Because HTML‑Entry shares the same document, global event listeners (e.g., onclick or addEventListener on body) can interfere across apps; developers must follow coding conventions.
Deployment requires manual CORS handling.
Future considerations
Shared component and library reuse to avoid duplicate implementations.
Automating micro‑app registration via scripts to streamline the migration process.
Conclusion
Overall, qiankun provides strong out‑of‑the‑box usability for micro‑frontend integration with minimal configuration changes. The main challenges are related to routing consistency, CSS isolation, and cross‑origin setup, all of which can be resolved with the techniques described above.
If there are any mistakes or questions, feel free to comment. Thanks for reading!
This article was published by the NetEase Cloud Music Front‑End Team. Unauthorized reproduction is prohibited. The team is continuously hiring front‑end, iOS, and Android engineers. If you are interested, contact grp.music-fe (at) corp.netease.com .
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
