File: README.md
qiankun(乾坤)
[!WARNING]
🚧 qiankun 3.0 is currently under active development — check out the Roadmap for details. Looking for qiankun 2.x? Its documentation lives at v2.qiankun.umijs.org.
In Chinese,
qian(乾)means heaven andkun(坤)earth.qiankunis the universe.
Qiankun enables you and your teams to build next-generation and enterprise-ready web applications leveraging Micro Frontends. It is inspired by and based on single-spa.
🤔 Motivation
A quick recap about the concept of Micro Frontends:
Techniques, strategies and recipes for building a modern web app with multiple teams using different JavaScript frameworks. — Micro Frontends
Qiankun was birthed internally in our group during the time web app development by distributed teams had turned to complete chaos. We faced every problem micro frontend was conceived to solve, so naturally, it became part of our solution.
The path was never easy, we stepped on every challenge there could possibly be. Just to name a few:
- In what form do micro-apps publish static resources?
- How does the framework integrate individual micro-apps?
- How to ensure that sub-applications are isolated from one another (development independence and deployment independence) and runtime sandboxed?
- Performance issues? What about public dependencies?
- The list goes on long ...
After solving these common problems of micro frontends and lots of polishing and testing, we extracted the minimal viable framework of our solution, and named it qiankun, as it can contain and serve anything. Not long after, it became the cornerstone of hundreds of our web applications in production, and we decided to open-source it to save you the suffering.
TLDR: Qiankun is probably the most complete micro-frontend solution you ever met🧐.
✨ Features
Qiankun inherits the fundamentals of single-spa — independent deployment, lazy loading, and a technology-agnostic host that never dictates a micro app's stack — and adds the pieces a real product needs:
- 📄 HTML entry — point qiankun at a URL, not a manifest of assets. The entry is streamed into the live document as it arrives, so a micro app starts rendering before its HTML has finished downloading.
- 🧳 JS sandbox — every micro app runs against a Proxy-membrane view of
windowanddocument, so globals, timers, listeners and dynamic DOM stay inside the app and are reclaimed on unmount. On by default. - 📦 Native ESM —
<script type="module">micro apps execute as real modules, routed through the membrane with dynamically injected import maps. Dynamicimport()works, and so does a Vite dev server. - 🛡 Style isolation — opt-in runtime scoping built on the CSS
@scopeat-rule, including external stylesheets. No rewriting your CSS, no Shadow DOM tax. - ⚡ Prefetch — warm a micro app's assets while the user is still elsewhere, so the switch feels instant.
- 🧩 UI bindings —
<MicroApp />components for React and Vue, with loader and error-boundary slots, so a micro app is just another component. - 🔧 Bundler plugins — one plugin for webpack 4/5 and Vite that marks the entry script and fixes the output library, replacing the boilerplate you used to hand-write.
- 🔬 Standalone sandbox —
@qiankunjs/sandboxis usable on its own to contain any third-party script, with no micro-frontend framework attached.
🚀 Getting started
[!NOTE]
v3 ships under the
rctag whilelateststill points at 2.x. Install with an explicit@rcto get it.
The fastest path is the agent skill — install it, then ask your coding agent (Claude Code, Cursor, …) to scaffold a host or a micro app wired up correctly:
npx skills add umijs/qiankunOr add qiankun to an existing host application:
npm i qiankun@rcLoad a micro app into any container element, and keep the returned handle to unmount it:
import { loadMicroApp } from 'qiankun';
const microApp = loadMicroApp({
name: 'react-app',
entry: '//localhost:7100',
container: document.getElementById('subapp-container'),
});
// when this part of the page goes away:
await microApp.unmount();If a micro app's activation is fully determined by the URL, the route-driven registerMicroApps + start APIs are the alternative orchestration model.
A micro app only has to export the three lifecycle hooks:
let root;
export async function bootstrap() {}
export async function mount(props: { container: HTMLElement }) {
root = render(props.container);
}
export async function unmount() {
root.unmount();
}That is the whole contract. See the quick start for the bundler configuration each stack needs.
💿 Examples
Every example is deployed and browsable at examples.qiankunjs.com — two host shells (React and Vue) mounting the same four micro apps, each running with the JS sandbox and style isolation on, plus a standalone sandbox lab. Every app carries an "isolation lab" that demonstrates what the sandbox actually contains: globals, leaked timers, and injected styles. The shells switch between English and 简体中文, and the choice travels to the micro apps as a prop — they re-render through the update lifecycle rather than remounting.
To run them locally:
git clone https://github.com/umijs/qiankun.git
cd qiankun
pnpm install
pnpm start:exampleThis builds the workspace packages and starts every app in parallel — open http://localhost:7099 for the React shell or http://localhost:7105 for the Vue one. See examples/README.md for what each app demonstrates.
📦 Packages
| Package | Version (click for changelogs) | Description |
|---|---|---|
| qiankun | The framework: registerMicroApps, loadMicroApp, start, prefetch |
|
| @qiankunjs/loader | Streaming HTML-entry loader | |
| @qiankunjs/sandbox | JS sandbox — usable standalone | |
| @qiankunjs/shared | Asset transpilers, fetch utilities, ESM-sandbox engine | |
| @qiankunjs/single-spa | Vendored single-spa fork the framework builds on | |
| @qiankunjs/react | <MicroApp /> for React |
|
| @qiankunjs/vue | <MicroApp /> for Vue |
|
| @qiankunjs/ui-shared | Shared internals of the UI bindings | |
| @qiankunjs/bundler-plugin | webpack 4/5 and Vite plugins for micro apps |
📖 Documentation
Full documentation lives at www.qiankunjs.com (English and 简体中文):
- Quick start — a running micro-frontend in five minutes
- Cookbook — style isolation, sandbox plugins, error handling, performance
- API reference — every option, typed
- FAQ — the questions that come up most
Design decisions are recorded as RFCs under docs/rfcs.
🎯 Roadmap
qiankun 3.0 is under active development. The plan, and the discussion around it, is in the 3.0 Roadmap.
🤝 Contributing
Issues and pull requests are welcome. The repository is a pnpm monorepo that requires Node ^22.15 || >=24 and pnpm@11:
pnpm install # install every workspace package
pnpm run build # build packages and examples
pnpm run test # unit tests (vitest, no build needed)
pnpm run test:e2e # end-to-end tests (Playwright, against built output)
pnpm run ci # what CI runs: build + eslint + prettierCommits follow Conventional Commits — releases and changelogs are derived from them, so do not hand-write changesets. AGENTS.md documents the architecture, conventions and anti-patterns in depth.
👥 Contributors
Thanks to all the contributors!
🎁 Acknowledgements
- single-spa — what an awesome meta-framework for micro-frontends!
- writable-dom — utility to stream HTML content into a live document.
📄 License
Qiankun is MIT licensed.
File: docs/guide/getting-started.md
Getting started
This guide creates and runs a main app and a micro-app with the official Agent skill. The main app controls the micro-app directly with loadMicroApp.
You need Node.js >=20.19, npm, and a modern Chromium-based browser or Safari (see Browser support for the full requirements).
Create and run the applications
From an empty working directory, install the skill, then have your coding agent (Claude Code, Cursor, …) create the two projects:
mkdir qiankun-demo
cd qiankun-demo
npx skills add umijs/qiankunDescribe the goal to the agent, for example:
Use qiankun to create a React + TypeScript main app named main-app (port 7099, loading the micro-app with loadMicroApp) and a React + TypeScript micro-app named sub-app (port 7101)
If you are not working with an agent, follow the tutorial to build the same structure by hand — the rest of this page applies either way.
Once the projects exist, start them in two terminals, both opened from qiankun-demo:
::: code-group
cd sub-app
npm install
npm run dev
# http://localhost:7101cd main-app
npm install
npm run dev
# http://localhost:7099:::
Open http://localhost:7099. The page now contains the independently served micro-app. You can also open http://localhost:7101 to confirm that the micro-app still runs by itself.
For the skill's installation details, coverage, and what it generates, see Agent skill.
::: warning Firefox and ESM applications
The ESM sandbox depends on dynamically injected import maps, which Firefox does not currently support. Use a Chromium-based browser or Safari for this guide. Classic micro-apps are unaffected.
:::
How the main app controls the micro-app
The generated main app runs on port 7099. Its App.tsx follows this pattern after React has created the container element:
import { loadMicroApp } from 'qiankun';
import { useEffect, useRef } from 'react';
export default function App() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const container = containerRef.current;
if (!container) return;
const microApp = loadMicroApp({
name: 'sub-app',
entry: '//localhost:7101',
container,
});
return () => {
void microApp.unmount().catch((error: unknown) => {
console.error('Failed to unmount sub-app:', error);
});
};
}, []);
return ;
}The application description has three required fields:
| Field | Purpose |
|---|---|
name |
Identifies this micro-app. |
entry |
Points to its HTML entry; here, the server on port 7101. |
container |
The HTMLElement that receives the micro-app. |
loadMicroApp returns a MicroApp handle. Keep that handle for as long as the instance is in use and call unmount() during cleanup. This lets qiankun run the micro-app's unmount lifecycle and release the instance cleanly. React effect cleanup cannot return a Promise, so the example starts unmounting and attaches a rejection handler; in an async host flow, await unmount() before removing the container.
What the micro-app provides
The generated micro-app is an ordinary Vite application with two additions:
@qiankunjs/bundler-pluginprepares its HTML entry and development server for qiankun.- Its entry module exports
bootstrap,mount, andunmount.mountrenders inside the container supplied by the main app, andunmountdestroys the framework root.
The standalone branch renders the same application when it is opened directly on port 7101. You therefore keep independent development while also making the app loadable by a main app.
Route-driven applications
loadMicroApp is the primary API when application code decides when an instance exists. If activation should instead be derived entirely from the current URL, use registerMicroApps together with start. You do not need either API for the flow on this page.
Next steps
- Build the same setup step by step in the tutorial.
- Learn the application contract in Lifecycle and props.
- Prepare an existing Vite or Webpack application.
- See every option and method in the
loadMicroAppAPI.
File: docs/guide/getting-started.zh.md
toc: menu
快速上手
主应用
1. 安装 qiankun
$ yarn add qiankun # 或者 npm i qiankun -S2. 在主应用中注册微应用
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react app', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule',
},
{
name: 'vue app',
entry: { scripts: ['//localhost:7100/main.js'] },
container: '#yourContainer2',
activeRule: '/yourActiveRule2',
},
]);
start();当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。
如果微应用不是直接跟路由关联的时候,你也可以选择手动加载微应用的方式:
import { loadMicroApp } from 'qiankun';
loadMicroApp({
name: 'app',
entry: '//localhost:7100',
container: '#yourContainer',
});微应用
微应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。
1. 导出相应的生命周期钩子
微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('react app bootstraped');
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(
props.container ? props.container.querySelector('#root') : document.getElementById('root'),
);
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log('update props', props);
}qiankun 基于 single-spa,所以你可以在这里找到更多关于微应用生命周期相关的文档说明。
无 webpack 等构建工具的应用接入方式请见这里
2. 配置微应用的打包工具
除了代码中暴露出相应的生命周期钩子之外,为了让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置:
webpack:
webpack v5:
const packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
},
};webpack v4:
const packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};相关配置介绍可以查看 webpack 相关文档。
File: docs/guide/README.md
nav: order: 0 toc: menu
Introduction
Qiankun is an implementation of Micro Frontends, which based on single-spa. It aims to make it easier and painless to build a production-ready microfront-end architecture system.
Qiankun hatched from Ant Financial’s unified front-end platform for cloud products based on micro-frontends architecture. After full testing and polishing of a number of online applications, we extracted its micro-frontends kernel and open sourced it. We hope to help the systems who has the same requirement more convenient to build its own micro-frontends application in the community. At the same time, with the help of community, qiankun will be polished and improved.
At present qiankun has served more than 2000 online applications inside Ant, and it is definitely trustworthy in terms of ease of use and completeness.
What Are Micro FrontEnds
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. -- Micro Frontends
Micro Frontends architecture has the following core values:
Technology Agnostic
The main framework does not restrict access to the technology stack of the application, and the sub-applications have full autonomy.
Independent Development and Deployment
The sub application repo is independent, and the frontend and backend can be independently developed. After deployment, the main framework can be updated automatically.
Incremental Upgrade
In the face of various complex scenarios, it is often difficult for us to upgrade or refactor the entire technology stack of an existing system. Micro frontends is a very good method and strategy for implementing progressive refactoring.
Isolated Runtime
State is isolated between each subapplication and no shared runtime state.
The micro-frontends architecture is designed to solve the application of a single application in a relatively long time span. As a result of the increase in the number of people and teams involved, it has evolved from a common application to a Frontend Monolith then becomes unmaintainable. Such a problem is especially common in enterprise web applications.
For more related introductions about micro frontends, I recommend that you check out these articles:
Core Design Philosophy Of qiankun
🥄 Simple
Since the main application sub-applications can be independent of the technology stack, qiankun is just a jQuery-like library for users. You need to call several qiankun APIs to complete the micro frontends transformation of your application. At the same time, due to the design of qiankun's HTML entry and sandbox, accessing sub-applications is as simple as using an iframe.
🍡 Decoupling/Technology Agnostic
As the core goal of the micro frontends is to disassemble the monolithic application into a number of loosely coupled micro applications that can be autonomous, all the designs of qiankun are follow this principle, such as HTML Entry, sandbox, and communicating mechanism between applications. Only in this way can we ensure that sub-applications truly have the ability to develop and run independently.
How Does Qiankun Works
TODO
Why Not Iframe
Check this article Why Not Iframe
Features
- 📦 Based On single-spa , provide a more out-of-box APIs.
- 📱 Technology Agnostic,any javascript framework can use/integrate, whether React/Vue/Angular/JQuery or the others.
- 💪 HTML Entry access mode, allows you to access the son as simple application like use the iframe.
- 🛡 Style Isolation, make sure styles don't interfere with each other.
- 🧳 JS Sandbox, ensure that global variables/events do not conflict between sub-applications.
- ⚡ Prefetch Assets, prefetch unopened sub-application assets during the browser idle time to speed up the sub-application opening speed.
- 🔌 Umi Plugin, @umijs/plugin-qiankun is provided for umi applications to switch to a micro frontends architecture system with one line code.
File: docs/guide/README.zh.md
nav: title: 指南 order: 0 toc: menu
介绍
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
qiankun 孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台,在经过一批线上应用的充分检验及打磨后,我们将其微前端内核抽取出来并开源,希望能同时帮助社区有类似需求的系统更方便的构建自己的微前端系统,同时也希望通过社区的帮助将 qiankun 打磨的更加成熟完善。
目前 qiankun 已在蚂蚁内部服务了超过 2000+ 线上应用,在易用性及完备性上,绝对是值得信赖的。
什么是微前端
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. -- Micro Frontends
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端架构具备以下几个核心价值:
技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
独立运行时
每个微应用之间状态隔离,运行时状态不共享
微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
更多关于微前端的相关介绍,推荐大家可以去看这几篇文章:
qiankun 的核心设计理念
🥄 简单
由于主应用微应用都能做到技术栈无关,qiankun 对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。同时由于 qiankun 的 HTML entry 及沙箱的设计,使得微应用的接入像使用 iframe 一样简单。
🍡 解耦/技术栈无关
微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用,而 qiankun 的诸多设计均是秉持这一原则,如 HTML entry、沙箱、应用间通信等。这样才能确保微应用真正具备 独立开发、独立运行 的能力。
它是如何工作的
TODO
为什么不是 iframe
看这里 Why Not Iframe
特性
- 📦 基于 single-spa 封装,提供了更加开箱即用的 API。
- 📱 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。
- 💪 HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。
- 🛡 样式隔离,确保微应用之间样式互相不干扰。
- 🧳 JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
- ⚡️ 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
- 🔌 umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。
File: docs/faq/README.md
nav: title: FAQ toc: menu
FAQ
`Application died in status LOADING_SOURCE_CODE: You need to export the functional lifecycles in xxx entry`
This error thrown as qiankun could not find the exported lifecycle method from your entry js.
To solve the exception, try the following steps:
check you have exported the specified lifecycles, see the doc
check you have set the specified configuration with your bundler, see the doc
Check the webpack of micro app whether it configured with
output.globalObjector not, be sure its value waswindowif it had, or remove it to use default value.Check your
package.jsonname field is unique between sub apps.Check if the entry js in the sub-app's entry HTML is the last script to load. If not, move the order to make it be the last, or manually mark the entry js as
entryin the HTML, such as:html {2}<script src="/antd.js"></script> <script src="/appEntry.js" entry></script> <script src="https://www.google.com/analytics.js"></script>If the development environment is OK but the production environment is not, check whether the
index.htmlandentry jsof the micro app are returned normally, for example,404.htmlis returned.If you're using webpack5 and not using module federation, please see here
If you are using webpack5 and using module federation, you need to expose the life cycle function in the index file, and then expose the life cycle function externally in the bootstrap file.
const promise = import('index');
export const bootstrap = () => promise.then((m) => m.bootstrap());
export const mount = () => promise.then((m) => m.mount());
export const unmount = () => promise.then((m) => m.unmount());Check whether the main app and micro-app use AMD or CommonJS. Check method: run the main app and the micro-app independently, and enter the following code in the console:
(typeof exports === 'object' && typeof module === 'object') || (typeof define === 'function' && define.amd) || typeof exports === 'object',If it returnstrue,that it is caused by this reason, and there are mainly the following two solutions:- Solution 1: Modify the
libraryTargetof the micro-appwebpackto'window'.
diffconst packageName = require('./package.json').name; module.exports = { output: { library: `${packageName}-[name]`, - libraryTarget: 'umd', + libraryTarget: 'window', jsonpFunction: `webpackJsonp_${packageName}`, }, };- Solution 2: The micro-app is not bundle with
umd, directly mount the life cycle function to thewindowin the entry file, refer toMicro app built without webpack.
- Solution 1: Modify the
If it still not works after the steps above, this is usually due to browser compatibility issues. Try to set the webpack
output.libraryof the broken sub app the same with your main app registration for your app, such as:
Such as here is the main configuration:
// main app
registerMicroApps([
{
name: 'brokenSubApp',
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/react',
},
]);Set the output.library the same with main app registration:
module.exports = {
output: {
// Keep the same with the registration in main app
library: 'brokenSubApp',
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};`Application died in status NOT_MOUNTED: Target container with #container not existed after xxx mounted!`
This error thrown as the container DOM does not exist after the micro app is loaded. The possible reasons are:
The root id of the micro app conflicts with other DOM, and the solution is to modify the search range of the root id.
vuemicro app:jsfunction render(props = {}) { const { container } = props; instance = new Vue({ router, store, render: (h) => h(App), }).$mount(container ? container.querySelector('#app') : '#app'); } export async function mount(props) { render(props); }reactmicro app:jsfunction render(props) { const { container } = props; ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root')); } export async function mount(props) { render(props); } export async function unmount(props) { const { container } = props; ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root')); }Some js of micro app use
document.write, such as AMAP 1.x version, Tencent Map 2.x version.If it is caused by the map js, see if the upgrade can be resolved, for example, upgrade the AMAP map to version 2.x.
If the upgrade cannot be resolved, it is recommended to put the map on the main app to load. The micro app also introduces this map js (used in run independently), but add the
ignoreattribute to the<script>tag:html<script src="https://map.qq.com/api/gljs?v=1.exp" ignore></script>In other cases, please do not use
document.write.
`Application died in status NOT_MOUNTED: Target container with #container not existed while xxx mounting!`
This error usually occurs when the main app is Vue, and the container is written on a routing page and uses the routing transition effect. Some special transition effect caused the container not to exist in the mounting process of the micro app. The solution is to use other transition effects, or remove the routing transition.
`Application died in status NOT_MOUNTED: Target container with #container not existed while xxx loading!`
Similar to the above error, This error thrown as the container DOM does not exist when the micro app is loaded. Generally, it is caused by incorrect calling timing of the start function, just adjust the calling timing of the start function.
How to determine the completion of the container DOM loading? The vue app can be called during the mounted life cycle, and the react app can be called during the componentDidMount life cycle.
If it still reports an error, check whether the container DOM is placed on a routing page of the main app, please refer to [How to load micro apps on a routing page of the main app](#How to load micro apps on a routing page of the main app)
`[import-html-entry]: error occurs while excuting xxx script http://xxx.xxx.xxx/x.js`
The first line is just a helper info printed by qiankun via console.error to help users identify which js file threw the error faster. It is not an exception thrown by qiankun itself.
The actual exception info is in the second line.
For example in the error above, it means the child app itself threw an exception when executing http://localhost:9100/index.bundle.js. And the actual exception message is Uncaught TypeError: Cannot read property 'call' of undefined in the second line.
Exceptions from the child app itself can be debugged and fixed with the following steps:
- Based on the specific exception message, check if the js file that errors has syntax errors, like missing semicolons, depending on uninitialized variables etc.
- Whether it depends on global variables provided by the main app, but the main app did not initialize them.
- Compatibility issues. The child app js itself has syntax compatibility issues in the current runtime environment.
How to load micro apps on a routing page of the main app
It must be ensured that the routing page of the main app is also loaded when the micro app is loaded.
vue + vue-router main app:
- When the main app registers this route, add a
*topath, Note: If this route has other sub-routes, you need to register another route, just use this component.jsconst routes = [ { path: '/portal/*', name: 'portal', component: () => import('../views/Portal.vue'), }, ]; - The
activeRuleof the micro app needs to include the routepathof the main app.jsregisterMicroApps([ { name: 'app1', entry: 'http://localhost:8080', container: '#container', activeRule: '/portal/app1', }, ]); - Call the
startfunction in themountedcycle of thePortal.vuecomponent, be careful not to call it repeatedly.jsimport { start } from 'qiankun'; export default { mounted() { if (!window.qiankunStarted) { window.qiankunStarted = true; start(); } }, };
react + react-router main app:only need to make the activeRule of the sub app include the route of the main app.
angular + angular-router main app,similar to the Vue app:
The main app registers a wildcard sub route for this route, and the content is empty.
tsconst routes: Routes = [ { path: 'portal', component: PortalComponent, children: [{ path: '**', component: EmptyComponent }], }, ];The
activeRuleof the micro app needs to include the routepathof the main app.jsregisterMicroApps([ { name: 'app1', entry: 'http://localhost:8080', container: '#container', activeRule: '/portal/app1', }, ]);Call the
startfunction in thengAfterViewInitcycle of this routing component, be careful not to call it repeatedly.tsimport { start } from 'qiankun'; export class PortalComponent implements AfterViewInit { ngAfterViewInit(): void { if (!window.qiankunStarted) { window.qiankunStarted = true; start(); } } }
Vue Router Error - `Uncaught TypeError: Cannot redefine property: $router`
If you pass { sandbox: true } to start() function, qiankun will use Proxy to isolate global window object for sub applications. When you access window.Vue in sub application's code,it will check whether the Vue property in the proxyed window object. If the property does not exist, it will look it up in the global window object and return it.
There are three lines code in the vue-router as followed, and it will access window.Vue once the vue-router module is loaded. And the window.Vue in following code is your master application's Vue.
if (inBrowser && window.Vue) {
window.Vue.use(VueRouter);
}To solve the error, choose one of the options listed below:
- Use bundler to pack
Vuelibrary, instead of CDN or external module - Rename
Vueto other name in master application, eg:window.Vue2 = window.Vue; delete window.Vue
Why dynamic imported assets missing?
The reason is that webpack does not use the correct publicPath when loading the resource.
Two way to solve that:
1. With webpack live public path config
qiankun will inject a live public path variable before your sub app bootstrap, what you need is to add this code at the top of your sub app entry js:
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;For more details, check the webpack doc.
2. With webpack static public path config
You need to set your publicPath configuration to an absolute url, and in development with webpack it might be:
{
output: {
publicPath: `//localhost:${port}`;
}
}After the micro-app is bundled, the font files and images in the css load 404
The reason is that qiankun changed the external link style to the inline style, but the loading path of the font file and background image is a relative path.
Once the css file is packaged, you cannot modify the path of the font file and background image by dynamically modifying the publicPath.
There are mainly the following solutions:
Upload all static resources such as pictures to
cdn, and directly reference the address ofcdnincss(recommended)Use the
url-loaderofwebpackto package font files and images asbase64(suitable for projects with small font files and images)(recommended)
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp|woff2?|eot|ttf|otf)$/i,
use: [
{
loader: 'url-loader',
options: {},
},
],
},
],
},
};vue-cli3 project:
module.exports = {
chainWebpack: (config) => {
config.module.rule('fonts').use('url-loader').loader('url-loader').options({}).end();
config.module.rule('images').use('url-loader').loader('url-loader').options({}).end();
},
};vue-cli5 project, use the asset/inline of webpack replace url-loader:
module.exports = {
chainWebpack: (config) => {
config.module.rule('fonts').type('asset/inline').set('generator', {});
config.module.rule('images').type('asset/inline').set('generator', {});
},
};- Use the
file-loaderofwebpackto inject the full path when packaging it (suitable for projects with large font files and images)
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]',
publicPath,
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
},
},
],
},
],
},
};vue-cli3 project:
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
chainWebpack: (config) => {
const fontRule = config.module.rule('fonts');
fontRule.uses.clear();
fontRule
.use('file-loader')
.loader('file-loader')
.options({
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
})
.end();
const imgRule = config.module.rule('images');
imgRule.uses.clear();
imgRule
.use('file-loader')
.loader('file-loader')
.options({
name: 'img/[name].[hash:8].[ext]',
publicPath,
})
.end();
},
};- Combine the two schemes, convert small files to
base64, and inject path prefixes for large files
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/i,
use: [
{
loader: 'url-loader',
options: {},
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]',
publicPath,
},
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
use: [
{
loader: 'url-loader',
options: {},
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
},
},
},
],
},
],
},
};vue-cli3 project:
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
chainWebpack: (config) => {
config.module
.rule('fonts')
.use('url-loader')
.loader('url-loader')
.options({
limit: 4096, // Less than 4kb will be packaged as base64
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
},
},
})
.end();
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.options({
limit: 4096, // Less than 4kb will be packaged as base64
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]',
publicPath,
},
},
});
},
};- The
vue-cli3project can packagecssintojswithout generating files separately (not recommended, only suitable for projects with lesscss)
Configuration reference vue-cli3 official website:
module.exports = {
css: {
extract: false,
},
};Must a sub app asset support cors?
Yes it is.
Since qiankun get assets which imported by sub app via fetch, these static resources must be required to support cors.
If it is your own script, you can support it by developing server-side cross-domain. If it is a 3-legged script and cannot add cross-domain headers to it, you can drag the script to the local and have your own server serve support cross-domain.
See Enable Nginx Cors.
How to solve that micro apps loaded failed due to abnormal scripts inserted dynamically by carriers
Scripts inserted by carriers are usually marked with async to avoid loading of block micro apps. This is usually no problem, such as:
<script async src="//www.rogue.com/rogue.js"></script>However, if some inserted scripts are not marked as async, once such scripts fail to run, the entire application will be blocked and subsequent scripts will no longer be executed. We can solve this problem in the following ways:
Use a custom `getTemplate` method
Filter abnormal scripts in the HTML template of the micro app through the getTemplate method implemented by yourself.
import { start } from 'qiankun';
start({
getTemplate(tpl) {
return tpl.replace('<script src="/to-be-replaced.js"><script>', '');
},
});Use custom fetch method
Intercept abnormal scripts through the fetch method implemented by yourself.
import { start } from 'qiankun';
start({
async fetch(url, ...args) {
if (url === 'http://to-be-replaced.js') {
return {
async text() {
return '';
},
};
}
return window.fetch(url, ...args);
},
});Change the response Content-type of micro app's HTML to `text/plain` (ultimate solution)
The principle is that the carriers can only recognize the request whose response's content-type is text/html and insert the script, and the response of the text/plain type will not be hijacked.
How to modify the content-type of the response header of the micro-application HTML request, you can google it yourself, and there is a simpler and more efficient solution:
Copy an
index.txtfile fromindex.htmlwhen the micro-app is published.Change
entryin the main app to a txt address, for example:diffregisterMicroApps( [ - { name: 'app1', entry: '//localhost:8080/index.html', container, activeRule }, + { name: 'app1', entry: '//localhost:8080/index.txt', container, activeRule }, ], );
How to guarantee the main app stylesheet isolated with sub apps?
Qiankun will isolate stylesheet between your sub apps automatically, you can manually ensure isolation between master and child applications. Such as add a prefix to all classes in the master application, and if you are using ant-design, you can follow this doc to make it works.
Example for antd:
use webpack to modify antd less variable
diff{ loader: 'less-loader', + options: { + modifyVars: { + '@ant-prefix': 'yourPrefix', + }, + javascriptEnabled: true, + }, }set antd ConfigProvider
jsximport { ConfigProvider } from 'antd'; export const MyApp = () => ( <ConfigProvider prefixCls="yourPrefix"> <App /> </ConfigProvider> );
Detailed documentation pls check antd official guide.
How to make sub app to run independently?
Use the builtin global variable to identify the environment which provided by qiankun master:
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export const mount = async () => render();Could I active two sub apps at the same time?
When the subapp should be active depends on your activeRule config, like the example below, we set activeRule logic the same between reactApp and react15App:
registerMicroApps([
// define the activeRule by your self
{ name: 'reactApp', entry: '//localhost:7100', container, activeRule: () => window.isReactApp },
{ name: 'react15App', entry: '//localhost:7102', container, activeRule: () => window.isReactApp },
{ name: 'vue app', entry: '//localhost:7101', container, activeRule: () => window.isVueApp },
]);
start({ singular: false });After setting singular: false in start method, reactApp and react15App should be active at the same time once isReactApp method returns true.
How to extract the common library?
Don’t share a runtime, even if all teams use the same framework. - Micro Frontends
Although sharing dependencies isn't a good idea, but if you really need it, you can external the common dependencies from sub apps and then import them in master app.
In the future qiankun will provide a smarter way to make it automatically.
Does qiankun compatible with ie?
Yes.
However, the IE environment (browsers that do not support Proxy) can only use the single-instance pattern, where the singular configuration will be set true automatically by qiankun if IE detected.
You can find the singular usage here.
How to polyfill IE?
If you want qiankun (or its dependent libraries, or your own application) to work properly in IE, you need to introduce the following polyfills at the portal at least:
import 'whatwg-fetch';
import 'custom-event-polyfill';
import 'core-js/stable/promise';
import 'core-js/stable/symbol';
import 'core-js/stable/string/starts-with';
import 'core-js/web/url';We recommend that you use @babel/preset-env plugin directly to polyfill IE automatically, all the instructions for @babel/preset-env you can found in babel official document.
Error `Here is no "fetch" on the window env, you need to polyfill it`
Qiankun use window.fetch to get resources of the micro applications, but some browsers does not support it, you should get the polyfill in the entry.
How to handle subapplication JSONP cross-domain errors?
qiankun will convert the dynamic script loading of the subapplication (such as JSONP) into a fetch request, so the corresponding back-end service needs to support cross-domain, otherwise it will cause an error.
In singular mode, you can use the excludeAssetFilter parameter to release this part of the resource request, but note that the resources released by this option will escape the sandbox, and the resulting side effects need to be handled by you.
If you use JSONP in not-singular mode, simply using excludeAssetFilter does not achieve good results, because each application is isolated by the sandbox; you can provide a unified JSONP tool in the main application, and the subapplication just calls the tool.
404 after refresh of child application?
It is usually because you are routing in Browser mode, which requires the server to open it. Specific configuration mode reference:
How to configure the 404 page in the main application?
First of all, you cannot use the wildcard *. You can register the 404 page as a normal routing page, such as /404, and then judge in the routing hook function of the main project, if it is neither the main application routing nor the micro application , Then jump to the 404 page.
Take vue-router as an example, the pseudo code is as follows:
const childrenPath = ['/app1', '/app2'];
router.beforeEach((to, from, next) => {
if (to.name) {
// There is a name attribute, indicating that it is the route of the main project
return next();
}
if (childrenPath.some((item) => to.path.includes(item))) {
return next();
}
next({ name: '404' });
});How to jump between micro apps?
It is not feasible to use the router instance of micro-application directly to jump between micro-applications or micro-applications to main application page, such as the Link component in react-router or router-link in vue, because the router instance jump of micro-applications is based on the 'base' of routes. There are such ways to jump:
history. PushState (): [MDN usage introduction] (https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState)- Direct use of native a full address label, such as:
< a href = "http://localhost:8080/app1" > app1 < / a > - Modify the location href jump, such as:
window. The location. The href = 'http://localhost:8080/app1'
After the microapp file is updated, the old version of the file is still accessed
The server needs to configure a response header for the index.html of the micro application: Cache-Control no-cache, which means to check whether it is updated every time it requests.
Take Nginx as an example:
location = /index.html {
add_header Cache-Control no-cache;
}micro app styles was lost when using config entry
Some scenarios we had to use config entry to load micro app (** not recommended **):
loadMicroApp({
name: 'configEntry',
entry: {
scripts: ['//t.com/t.js'],
styles: ['//t.com/t.css'],
},
});Since there is no HTML attached to entry JS for microapp, the mount hook simply says:
export async function mount(props) {
ReactDOM.render(<App />, props.container);
}As props.container is not an empty container and will contain information such as the style sheet that the microapp registers through the styles configuration, when we render directly for the container that the react application is applying with 'props.container', all the original DOM structures in the container will be overwritten, causing the style sheet to be lost.
We need to build an empty render container for micro applications that use Config Entry to mount react applications:
loadMicroApp({
name: 'configEntry',
entry: {
+ html: '',
scripts: ['//t.com/t.js'],
styles: ['//t.com/t.css']
}
});The mount hook is not directly render to props.container, but to its 'root' node:
export async function mount(props) {
- ReactDOM.render(<App/>, props.container);
+ ReactDOM.render(<App/>, props.container.querySelector('#root'));
}How to solve the problem that cookies are not carried when pulling micro-app entries
As the requests to pull micro-app entry are all cross-domain, when your micro-app relies on cookies (such as authentication), you need to customize the fetch method to enable the cors mode:
If you load the microapps through registerMicroApps, you need to configure a custom fetch in the start method, such as:
jsimport { start } from 'qiankun'; start({ fetch(url, ...args) { // Enable cors mode for the specified microapp if (url === 'http://app.alipay.com/entry.html') { return window.fetch(url, { ...args, mode: 'cors', credentials: 'include', }); } return window.fetch(url, ...args); }, });If you load the microapp via loadMicroApp, you need to configure a custom fetch when invoking, such as:
jsimport { loadMicroApp } from 'qiankun'; loadMicroApp(app, { fetch(url, ...args) { // Enable cors mode for the specified microapp if (url === 'http://app.alipay.com/entry.html') { return window.fetch(url, { ...args, mode: 'cors', credentials: 'include', }); } return window.fetch(url, ...args); }, });If you are through umi plugin to use qiankun,then you only need to enable the credentials configuration for the corresponding microapp:
diffexport default { qiankun: { master: { apps: [ { name: 'app', entry: '//app.alipay.com/entry.html', + credentials: true, } ] } } }
How to solve the problem that adding event handlers to window objects by subapplication does not take effect
Since the window object accessed by the sub-application is the object after being proxed by qiankun, it is invalid to add an event handler directly to the window object, and you can solve the problem by adding an event listener to the window by addEventListener:
window.addEventListener('eventName', eventHandler);File: docs/faq/README.zh.md
nav: title: 常见问题 toc: menu
常见问题
`Application died in status LOADING_SOURCE_CODE: You need to export the functional lifecycles in xxx entry`
qiankun 抛出这个错误是因为无法从微应用的 entry js 中识别出其导出的生命周期钩子。
可以通过以下几个步骤解决这个问题:
检查微应用是否已经导出相应的生命周期钩子,参考文档。
检查微应用的 webpack 是否增加了指定的配置,参考文档。
检查微应用的 webpack 是否配置了
output.globalObject的值,如果有请确保其值为window,或者移除该配置从而使用默认值。检查微应用的
package.json中的name字段是否是微应用中唯一的。检查微应用的 entry html 中入口的 js 是不是最后一个加载的脚本。如果不是,需要移动顺序将其变成最后一个加载的 js,或者在 html 中将入口 js 手动标记为
entry,如:html {2}<script src="/antd.js"></script> <script src="/appEntry.js" entry></script> <script src="https://www.google.com/analytics.js"></script>如果开发环境可以,生产环境不行,检查微应用的
index.html和entry js是否正常返回,比如说返回了404.html。如果你正在使用 webpack5,但没用使用模块联邦,请看这个 issues。
如果你正在使用 webpack5,并且使用了使用模块联邦。需要在 index 文件中暴露生命周期函数,然后在 bootstrap 文件向外暴露生命周期函数。
const promise = import('index');
export const bootstrap = () => promise.then((m) => m.bootstrap());
export const mount = () => promise.then((m) => m.mount());
export const unmount = () => promise.then((m) => m.unmount());检查主应用和微应用是否使用了 AMD 或 CommonJS 模块化。检查方法:单独运行微应用和主应用,在控制台输入如下代码:
(typeof exports === 'object' && typeof module === 'object') || (typeof define === 'function' && define.amd) || typeof exports === 'object',如果返回true,则说明是这种情况,主要有以下两个解决办法:- 解决办法 1:修改微应用
webpack的libraryTarget为'window'。
diffconst packageName = require('./package.json').name; module.exports = { output: { library: `${packageName}-[name]`, - libraryTarget: 'umd', + libraryTarget: 'window', jsonpFunction: `webpackJsonp_${packageName}`, }, };- 解决办法 2:微应用不打包成 umd ,直接在入口文件把生命周期函数挂载到 window 上,参考非 webpack 构建的微应用。
- 解决办法 1:修改微应用
如果在上述步骤完成后仍有问题,通常说明是浏览器兼容性问题导致的。可以尝试 将有问题的微应用的 webpack
output.library配置成跟主应用中注册的name字段一致,如:
假如主应用配置是这样的:
// 主应用
registerMicroApps([
{
name: 'brokenSubApp',
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/react',
},
]);将微应用的 output.library 改为跟主应用中注册的一致:
module.exports = {
output: {
// 这里改成跟主应用中注册的一致
library: 'brokenSubApp',
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};`Application died in status NOT_MOUNTED: Target container with #container not existed after xxx mounted!`
qiankun 抛出这个错误是因为微应用加载后容器 DOM 节点不存在了。可能的原因有:
微应用的根
id与其他 DOM 冲突。解决办法是:修改根id的查找范围。vue微应用:jsfunction render(props = {}) { const { container } = props; instance = new Vue({ router, store, render: (h) => h(App), }).$mount(container ? container.querySelector('#app') : '#app'); } export async function mount(props) { render(props); }react微应用:jsfunction render(props) { const { container } = props; ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root')); } export async function mount(props) { render(props); } export async function unmount(props) { const { container } = props; ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root')); }微应用的某些 js 里面使用了
document.write,比如高德地图 1.x 版本,腾讯地图 2.x 版本。如果是地图 js 导致的,先看看升级能否解决,比如说高德地图升级到 2.x 版本即可。
如果升级无法解决,建议将地图放到主应用加载,微应用也引入这个地图 js(独立运行时使用),但是给
<script>标签加上ignore属性:html<script src="https://map.qq.com/api/gljs?v=1.exp" ignore></script>如果是其他的情况,请不要使用
document.write。
`Application died in status NOT_MOUNTED: Target container with #container not existed while xxx mounting!`
这个报错通常出现在主应用为 vue 时,容器写在了路由页面并且使用了路由过渡效果,一些特殊的过渡效果会导致微应用在 mounting 的过程中容器不存在,解决办法就是换成其他的过渡效果,或者去掉路由过渡。
`Application died in status NOT_MOUNTED: Target container with #container not existed while xxx loading!`
与上面的报错类似,这个报错是因为微应用加载时容器 DOM 不存在。一般是因为 start 函数调用时机不正确导致的,调整 start 函数调用时机即可。
如何判断容器 DOM 加载完成?vue 应用可以在 mounted 生命周期调用,react 应用可以在 componentDidMount 生命周期调用。
如果仍然报错,检查容器 DOM 是否放在了主应用的某个路由页面,请参考如何在主应用的某个路由页面加载微应用。
`[import-html-entry]: error occurs while excuting xxx script http://xxx.xxx.xxx/x.js`
其中第一行只是 qiankun 通过 console.error 打印出来的一个辅助信息,目的是帮助用户更快的知道是哪个 js 报错了,并不是 qiankun 本身发生了异常。
真正的异常信息在第二行。
比如上图这样一个报错,指的是子应用在执行 http://localhost:9100/index.bundle.js 时,这个 js 本身抛异常了。而具体的异常信息就是第二行的 Uncaught TypeError: Cannot read property 'call' of undefined。
子应用本身的异常,可以尝试通过以下步骤排查解决:
- 根据具体的异常信息,检查报错的 js 是否有语法错误,比如少了分号、依赖了未初始化的变量等。
- 是否依赖了主应用提供的全局变量,但实际主应用并未初始化。
- 兼容性问题。子应用这个 js 本身在当前运行环境存在语法兼容性问题。
如何在主应用的某个路由页面加载微应用
必须保证微应用加载时主应用这个路由页面也加载了。
vue + vue-router 技术栈的主应用:
- 主应用注册这个路由时给
path加一个*,注意:如果这个路由有其他子路由,需要另外注册一个路由,仍然使用这个组件即可。jsconst routes = [ { path: '/portal/*', name: 'portal', component: () => import('../views/Portal.vue'), }, ]; - 微应用的
activeRule需要包含主应用的这个路由path。jsregisterMicroApps([ { name: 'app1', entry: 'http://localhost:8080', container: '#container', activeRule: '/portal/app1', }, ]); - 在
Portal.vue这个组件的mounted周期调用start函数,注意不要重复调用。jsimport { start } from 'qiankun'; export default { mounted() { if (!window.qiankunStarted) { window.qiankunStarted = true; start(); } }, };
react + react-router 技术栈的主应用:只需要让微应用的 activeRule 包含主应用的这个路由即可。
angular + angular-router 技术栈的主应用,与 vue 项目类似:
主应用给这个路由注册一个通配符的子路由,内容为空。
tsconst routes: Routes = [ { path: 'portal', component: PortalComponent, children: [{ path: '**', component: EmptyComponent }], }, ];微应用的
activeRule需要包含主应用的这个路由path。jsregisterMicroApps([ { name: 'app1', entry: 'http://localhost:8080', container: '#container', activeRule: '/portal/app1', }, ]);在这个路由组件的
ngAfterViewInit周期调用start函数,注意不要重复调用。tsimport { start } from 'qiankun'; export class PortalComponent implements AfterViewInit { ngAfterViewInit(): void { if (!window.qiankunStarted) { window.qiankunStarted = true; start(); } } }
Vue Router 报错 `Uncaught TypeError: Cannot redefine property: $router`
qiankun 中的代码使用 Proxy 去代理父页面的 window,来实现的沙箱,在微应用中访问 window.Vue 时,会先在自己的 window 里查找有没有 Vue 属性,如果没有就去父应用里查找。
在 VueRouter 的代码里有这样三行代码,会在模块加载的时候就访问 window.Vue 这个变量,微应用中报这个错,一般是由于父应用中的 Vue 挂载到了父应用的 window 对象上了。
if (inBrowser && window.Vue) {
window.Vue.use(VueRouter);
}可以从以下方式中选择一种来解决问题:
- 在主应用中不使用 CDN 等 external 的方式来加载
Vue框架,使用前端打包软件来加载模块 - 在主应用中,将
window.Vue变量改个名称,例如window.Vue2 = window.Vue; delete window.Vue
为什么微应用加载的资源会 404?
原因是 webpack 加载资源时未使用正确的 publicPath。
可以通过以下两个方式解决这个问题:
a. 使用 webpack 运行时 publicPath 配置
qiankun 将会在微应用 bootstrap 之前注入一个运行时的 publicPath 变量,你需要做的是在微应用的 entry js 的顶部添加如下代码:
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;关于运行时 publicPath 的技术细节,可以参考 webpack 文档。
b. 使用 webpack 静态 publicPath 配置
你需要将你的 webpack publicPath 配置设置成一个绝对地址的 url,比如在开发环境可能是:
{
output: {
publicPath: `//localhost:${port}`,
}
}微应用打包之后 css 中的字体文件和图片加载 404
原因是 qiankun 将外链样式改成了内联样式,但是字体文件和背景图片的加载路径是相对路径。
而 css 文件一旦打包完成,就无法通过动态修改 publicPath 来修正其中的字体文件和背景图片的路径。
主要有以下几个解决方案:
所有图片等静态资源上传至
cdn,css中直接引用cdn地址(推荐)借助
webpack的url-loader将字体文件和图片打包成base64(适用于字体文件和图片体积小的项目)(推荐)
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp|woff2?|eot|ttf|otf)$/i,
use: [
{
loader: 'url-loader',
options: {},
},
],
},
],
},
};vue-cli3 项目写法:
module.exports = {
chainWebpack: (config) => {
config.module.rule('fonts').use('url-loader').loader('url-loader').options({}).end();
config.module.rule('images').use('url-loader').loader('url-loader').options({}).end();
},
};vue-cli5 项目,使用 asset/inline 替代 url-loader,写法:
module.exports = {
chainWebpack: (config) => {
config.module.rule('fonts').type('asset/inline').set('generator', {});
config.module.rule('images').type('asset/inline').set('generator', {});
},
};- 借助
webpack的file-loader,在打包时给其注入完整路径(适用于字体文件和图片体积比较大的项目)
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]',
publicPath,
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
},
},
],
},
],
},
};vue-cli3 项目写法:
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
chainWebpack: (config) => {
const fontRule = config.module.rule('fonts');
fontRule.uses.clear();
fontRule
.use('file-loader')
.loader('file-loader')
.options({
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
})
.end();
const imgRule = config.module.rule('images');
imgRule.uses.clear();
imgRule
.use('file-loader')
.loader('file-loader')
.options({
name: 'img/[name].[hash:8].[ext]',
publicPath,
})
.end();
},
};- 将两种方案结合起来,小文件转
base64,大文件注入路径前缀
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/i,
use: [
{
loader: 'url-loader',
options: {},
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]',
publicPath,
},
},
},
],
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
use: [
{
loader: 'url-loader',
options: {},
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
},
},
},
],
},
],
},
};vue-cli3 项目写法:
const publicPath = process.env.NODE_ENV === 'production' ? 'https://qiankun.umijs.org/' : `http://localhost:${port}`;
module.exports = {
chainWebpack: (config) => {
config.module
.rule('fonts')
.use('url-loader')
.loader('url-loader')
.options({
limit: 4096, // 小于4kb将会被打包成 base64
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]',
publicPath,
},
},
})
.end();
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.options({
limit: 4096, // 小于4kb将会被打包成 base64
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]',
publicPath,
},
},
});
},
};vue-cli3项目可以将css打包到js里面,不单独生成文件(不推荐,仅适用于css较少的项目)
配置参考 vue-cli3 官网:
module.exports = {
css: {
extract: false,
},
};微应用静态资源一定要支持跨域吗?
是的。
由于 qiankun 是通过 fetch 去获取微应用的引入的静态资源的,所以必须要求这些静态资源支持跨域。
如果是自己的脚本,可以通过开发服务端跨域来支持。如果是三方脚本且无法为其添加跨域头,可以将脚本拖到本地,由自己的服务器 serve 来支持跨域。
参考:Nginx 跨域配置
如何解决由于运营商动态插入的脚本加载异常导致微应用加载失败的问题
运营商插入的脚本通常会用 async 标记从而避免 block 微应用的加载,这种通常没问题,如:
<script async src="//www.rogue.com/rogue.js"></script>但如果有些插入的脚本不是被标记成 async 的,这类脚本一旦运行失败,将会导致整个应用被 block 且后续的脚本也不再执行。我们可以通过以下几个方式来解决这个问题:
使用自定义的 getTemplate 方法
通过自己实现的 getTemplate 方法过滤微应用 HTML 模板中的异常脚本
import { start } from 'qiankun';
start({
getTemplate(tpl) {
return tpl.replace('<script src="/to-be-replaced.js"><script>', '');
},
});使用自定义的 fetch 方法
通过自己实现的 fetch 方法拦截有问题的脚本
import { start } from 'qiankun';
start({
async fetch(url, ...args) {
if (url === 'http://to-be-replaced.js') {
return {
async text() {
return '';
},
};
}
return window.fetch(url, ...args);
},
});将微应用的 HTML 的 response content-type 改为 text/plain(终极方案)
原理是运营商只能识别 response content-type 为 text/html 的请求并插入脚本,text/plain 类型的响应则不会被劫持。
修改微应用 HTML 的 content-type 方法可以自行 google,也有一个更简单高效的方案:
微应用发布时从 index.html 复制出一个 index.txt 文件出来
将主应用中的 entry 改为 txt 地址,如:
diffregisterMicroApps( [ - { name: 'app1', entry: '//localhost:8080/index.html', container, activeRule }, + { name: 'app1', entry: '//localhost:8080/index.txt', container, activeRule }, ], );
如何确保主应用跟微应用之间的样式隔离
qiankun 将会自动隔离微应用之间的样式(开启沙箱的情况下),你可以通过手动的方式确保主应用与微应用之间的样式隔离。比如给主应用的所有样式添加一个前缀,或者假如你使用了 ant-design 这样的组件库,你可以通过这篇文档中的配置方式给主应用样式自动添加指定的前缀。
以 antd 为例:
配置 webpack 修改 less 变量
diff{ loader: 'less-loader', + options: { + modifyVars: { + '@ant-prefix': 'yourPrefix', + }, + javascriptEnabled: true, + }, }配置 antd ConfigProvider
jsximport { ConfigProvider } from 'antd'; export const MyApp = () => ( <ConfigProvider prefixCls="yourPrefix"> <App /> </ConfigProvider> );
详细文档参考 antd 官方指南。
如何独立运行微应用?
有些时候我们希望直接启动微应用从而更方便的开发调试,你可以使用这个全局变量来区分当前是否运行在 qiankun 的主应用的上下文中:
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export const mount = async () => render();如何同时激活两个微应用?
微应用何时被激活完全取决于你的 activeRule 配置,比如下面的例子里,我们将 reactApp 和 react15App 的 activeRule 逻辑设置成一致的:
registerMicroApps([
// 自定义 activeRule
{ name: 'reactApp', entry: '//localhost:7100', container, activeRule: () => isReactApp() },
{ name: 'react15App', entry: '//localhost:7102', container, activeRule: () => isReactApp() },
{ name: 'vueApp', entry: '//localhost:7101', container, activeRule: () => isVueApp() },
]);
start({ singular: false });当在 start 方法中配置好 singular: false 后,只要 isReactApp() 返回 true 时,reactApp 和 react15App 将会同时被 mount。
如何提取出公共的依赖库?
不要共享运行时,即便所有的团队都是用同一个框架。- 微前端
虽然共享依赖并不建议,但如果你真的有这个需求,你可以在微应用中将公共依赖配置成 external,然后在主应用中导入这些公共依赖。
qiankun 2.0 版本将提供一种更智能的方式使其自动化。
qiankun 能兼容 ie 吗?
兼容.
但是 IE 环境下(不支持 Proxy 的浏览器)只能使用单实例模式,qiankun 会自动将 singular 配置为 true。
你可以在这里找到 singular 相关说明。
如何给 ie 打补丁?
如果希望 qiankun (或其依赖库、或者您的应用本身)在 IE 下正常运行,你至少需要在应用入口引入以下这些 polyfills:
import 'whatwg-fetch';
import 'custom-event-polyfill';
import 'core-js/stable/promise';
import 'core-js/stable/symbol';
import 'core-js/stable/string/starts-with';
import 'core-js/web/url';通常我们建议您直接使用 @babel/preset-env 插件完成自动引入 IE 需要的 polyfill 的能力,所有的操作文档您都可以在 babel 官方文档 找到。
报错 `Here is no "fetch" on the window env, you need to polyfill it`
qiankun 依赖的 import-html-entry 通过 window.fetch 来获取微应用的资源, 部分不支持 fetch 的浏览器需要在入口处打上相应的 polyfill
微应用 JSONP 跨域错误怎么处理?
qiankun 会将微应用的动态 script 加载(例如 JSONP)转化为 fetch 请求,因此需要相应的后端服务支持跨域,否则会导致错误。
在单实例模式下,你可以使用 excludeAssetFilter 参数来放行这部分资源请求,但是注意,被该选项放行的资源会逃逸出沙箱,由此带来的副作用需要你自行处理。
若在多实例模式下使用 JSONP,单纯使用 excludeAssetFilter 并不能取得好的效果,因为各应用被沙箱所隔离;你可以在主应用提供统一的 JSONP 工具,微应用调用主应用提供的该工具来曲线救国。
微应用路径下刷新后 404?
通常是因为你使用的是 browser 模式的路由,这种路由模式的开启需要服务端配合才行。具体配置方式参考:
主应用如何配置 404 页面?
首先不应该写通配符 * ,可以将 404 页面注册为一个普通路由页面,比如说 /404 ,然后在主应用的路由钩子函数里面判断一下,如果既不是主应用路由,也不是微应用,就跳转到 404 页面。
以vue-router为例,伪代码如下:
const childrenPath = ['/app1', '/app2'];
router.beforeEach((to, from, next) => {
if (to.name) {
// 有 name 属性,说明是主应用的路由
return next();
}
if (childrenPath.some((item) => to.path.includes(item))) {
return next();
}
next({ name: '404' });
});微应用之间如何跳转?
微应用之间的跳转,或者微应用跳主应用页面,直接使用微应用的路由实例是不行的,如 react-router 的 Link 组件或 vue 的 router-link,原因是微应用的路由实例跳转都基于路由的 base。有这几种办法可以跳转:
history.pushState():mdn 用法介绍- 直接使用原生 a 标签写完整地址,如:
[app1](http://localhost:8080/app1) - 修改 location href 跳转,如:
window.location.href = 'http://localhost:8080/app1'
微应用文件更新之后,访问的还是旧版文件
服务器需要给微应用的 index.html 配置一个响应头:Cache-Control no-cache,意思就是每次请求都检查是否更新。
以 Nginx 为例:
location = /index.html {
add_header Cache-Control no-cache;
}使用 config entry 时微应用样式丢失
有些场景下我们会使用 config entry 的方式加载微应用(不推荐):
loadMicroApp({
name: 'configEntry',
entry: {
scripts: ['//t.com/t.js'],
styles: ['//t.com/t.css'],
},
});微应用的 entry js 由于没有附属的 html,mount 钩子直接这么写的:
export async function mount(props) {
ReactDOM.render(<App />, props.container);
}因为 props.container 并不是一个空的容器,里面会包含微应用通过 styles 配置注册进来的样式表等信息,所以当我们直接以props.container 为 react 应用的容器渲染时,会把容器里原来的所有 dom 结构全部覆盖掉,从而导致样式表丢失。
我们需要给使用 config entry 的微应用构造一个空的渲染容器,专门用来挂载 react 应用:
loadMicroApp({
name: 'configEntry',
entry: {
+ html: '',
scripts: ['//t.com/t.js'],
styles: ['//t.com/t.css']
}
});mount 钩子里不是直接渲染到 props.container ,而是渲染到其 root 节点里:
export async function mount(props) {
- ReactDOM.render(<App/>, props.container);
+ ReactDOM.render(<App/>, props.container.querySelector('#root'));
}如何解决拉取微应用 entry 时 cookie 未携带的问题
因为拉取微应用 entry 的请求都是跨域的,所以当你的微应用是依赖 cookie (如登陆鉴权)的情况下,你需要通过自定义 fetch 的方式,开启 fetch 的 cors 模式:
如果你是通过 registerMicroApps 加载微应用的,你需要在 start 方法里配置自定义 fetch,如:
jsimport { start } from 'qiankun'; start({ fetch(url, ...args) { // 给指定的微应用 entry 开启跨域请求 if (url === 'http://app.alipay.com/entry.html') { return window.fetch(url, { ...args, mode: 'cors', credentials: 'include', }); } return window.fetch(url, ...args); }, });如果你是通过 loadMicroApp 加载微应用的,你需要在调用时配置自定义 fetch,如:
jsimport { loadMicroApp } from 'qiankun'; loadMicroApp(app, { fetch(url, ...args) { // 给指定的微应用 entry 开启跨域请求 if (url === 'http://app.alipay.com/entry.html') { return window.fetch(url, { ...args, mode: 'cors', credentials: 'include', }); } return window.fetch(url, ...args); }, });如果你是通过 umi plugin 来使用 qiankun 的,那么你只需要给对应的微应用开启 credentials 配置即可:
diffexport default { qiankun: { master: { apps: [ { name: 'app', entry: '//app.alipay.com/entry.html', + credentials: true, } ] } } }
如何解决子应用给 window 对象添加事件处理函数不生效的问题
由于子应用访问的 window 对象是被 qiankun 代理后的对象,因此直接给 window 对象添加事件处理函数是无效的,可以通过 addEventListener 给 window 添加事件监听器来解决该问题:
window.addEventListener('eventName', eventHandler);File: docs/cookbook/README.md
nav: title: Tutorial toc: menu
Tutorial
How to choose the routing mode of micro app
The three routes react-router, angular-router, and vue-router all support the hash and history modes. The different modes used by micro apps are slightly different in qiankun.
`activeRule` uses `location.pathname` to distinguish micro apps
When the main app uses location.pathname to distinguish micro apps, micro apps can be in hash and history modes.
When registering micro apps, activeRule needs to be written like this:
registerMicroApps([
{
name: 'app',
entry: 'http://localhost:8080',
container: '#container',
activeRule: '/app',
},
]);When the micro app is in
historymode, just set the routebase.When the micro app is in the
hashmode, the performance of the three routes is inconsistentrouting main app jump /app/#/aboutspecial configuration vue-router Response aboutroutingnone react-router not responding aboutroutingnone angular-router Response aboutroutingneed to set --base-hrefAngularapp set--base-hrefinpackage.json:diff- "start": "ng serve", + "start": "ng serve --base-href /angular9", - "build": "ng build", + "build": "ng build --base-href /angular9",After bundled and deployed, the
angularmicro app can be accessed by the main app, but the lazy-loaded route during independent access will report an error and the path is incorrect. There are two solutions:Solution 1: Modify
public-path.jsto:js__webpack_public_path__ = window.__POWERED_BY_QIANKUN__ ? window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ : `http://${ip}:${port}/`; // Fill in your actual deployment addressSolution 2: Modify the bundling command and deploy the micro app in the
angular9directory:diff- "build": "ng build", + "build": "ng build --base-href /angular9 --deploy-url /angular9/",
activeRule uses hash to distinguish micro apps
When the micro apps are all in the hash mode, hash can be used to distinguish the micro apps, and the routing mode of the main app is not limited.
When registering micro apps, activeRule needs to be written like this:
const getActiveRule = (hash) => (location) => location.hash.startsWith(hash);
registerMicroApps([
{
name: 'app-hash',
entry: 'http://localhost:8080',
container: '#container',
activeRule: getActiveRule('#/app-hash'),
// Here you can also write `activeRule:'#/app-hash'` directly,
// but if the main app is in history mode or the main app is deployed in a non-root directory, this writing will not take effect.
},
]);The react-router and angular-router micro-apps need to set the value of activeRule to the route's base, written in the same way as history.
In the hash mode of the vue-router app, the base for routing is not supported. You need to create an additional empty routing page and use all other routes as its children:
const routes = [
{
path: '/app-vue-hash',
name: 'Home',
component: Home,
children: [
// All other routes are written here
],
},
];When there are multiple micro apps at the same time
If a page displays multiple micro apps at the same time, you need to use loadMicroApp to load them.
If these micro apps have routing jump requirements, to ensure that these routes do not interfere with each other, you need to use the momery routing. vue-router uses the abstract mode, react-router uses the memory history mode, and angular-router does not support it.
How to deploy
Recommendation: The main app and micro apps are developed and deployed independently, that is, they belong to different git repositories and services.
Scenario 1: The main app and micro apps are deployed to the same server (the same IP and port)
If the number of servers is limited, or cannot be cross-domain and other reasons, the main app and micro apps need to be deployed together.
The usual practice is to deploy the main app in the first-level directory and the micro apps in the second/third-level directory.
If you want to deploy a micro app in a non-root directory, you need to do two things before bundling the micro app:
You must configure the
publicPathwhen buildingwebpackas the directory name. For more information, please see webpack official instructions 和 vue-cli3 official instructions.The micro app of the
historyroute needs to set thebase, the value is directory name, which is used when the micro app is accessed independently.
Pay attention to three points after deployment:
activeRulecannot be the same as the real access path of the micro app, otherwise it will directly become the micro app page when refreshed on the main app page.- The real access path of the micro app is the
entryof the micro app, and theentrycan be a relative path. - The
/at the end of theentrypath of the micro app cannot be omitted, otherwise thepublicPathwill be set incorrectly. For example, if the access path of the child item ishttp://localhost:8080/app1, thenentryIt ishttp://localhost:8080/app1/.
There are two specific deployment methods, choose one of them.
Solution 1: All micro apps are placed in a folder with a special name that do not have the same name as micro apps (**recommended**)
Suppose we have a main app and 6 micro apps ( respectively vue-hash, vue-history, react-hash, react-history, angular-hash, angular-history) And place it as follows after bundling:
└── html/ # root folder
|
├── child/ # the folder of all micro apps
| ├── vue-hash/ # the folder of the micro app `vue-hash`
| ├── vue-history/ # the folder of the micro app `vue-history`
| ├── react-hash/ # the folder of the micro app `react-hash`
| ├── react-history/ # the folder of the micro app `react-history`
| ├── angular-hash/ # the folder of the micro app `angular-hash`
| ├── angular-history/ # the folder of the micro app `angular-history`
├── index.html # index.html of the main app
├── css/ # the css folder of the main app
├── js/ # the js folder of the main appAt this time, you need to set the publicPath and the route base of the history mode when the micro app is built, and then bundle them into the corresponding directory.
| app | routing base | publicPath | real access path |
|---|---|---|---|
| vue-hash | none | /child/vue-hash/ | http://localhost:8080/child/vue-hash/ |
| vue-history | /child/vue-history/ | /child/vue-history/ | http://localhost:8080/child/vue-history/ |
| react-hash | none | /child/react-hash/ | http://localhost:8080/child/react-hash/ |
| react-history | /child/react-history/ | /child/react-history/ | http://localhost:8080/child/react-history/ |
| angular-hash | none | /child/angular-hash/ | http://localhost:8080/child/angular-hash/ |
| angular-history | /child/angular-history/ | /child/angular-history/ | http://localhost:8080/child/angular-history/ |
vue-historymicro appRouting's base configuration:
jsbase: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/child/vue-history/',Webpack's publicPath configuration(
vue.config.js):jsmodule.exports = { publicPath: '/child/vue-history/', };react-historymicro appRouting's base configuration:
htmlWebpack's publicPath configuration:
jsmodule.exports = { output: { publicPath: '/child/react-history/', }, };angular-historymicro appRouting's base configuration:
jsproviders: [ { provide: APP_BASE_HREF, useValue: window.__POWERED_BY_QIANKUN__ ? '/app-angular/' : '/child/angular-history/', }, ];The
publicPathof webpack is set bydeploy-url, modifypackage.json:diff- "build": "ng build", + "build": "ng build --deploy-url /child/angular-history/",
Then the registerMicroApps function at this time is like this (you need to ensure that activeRule and entry are different):
registerMicroApps([
{
name: 'app-vue-hash',
entry: '/child/vue-hash/', // http://localhost:8080/child/vue-hash/
container: '#container',
activeRule: '/app-vue-hash',
},
{
name: 'app-vue-history',
entry: '/child/vue-history/', // http://localhost:8080/child/vue-history/
container: '#container',
activeRule: '/app-vue-history',
},
// angular and react same as above
],So far, the main app and the micro apps can run normally, but the main app and the vue-history, react-history, and angular-history micro apps are history routes. The problem of refreshing 404 needs to be solved. nginx` also needs to be configured:
server {
listen 8080;
server_name localhost;
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /child/vue-history {
root html;
index index.html index.htm;
try_files $uri $uri/ /child/vue-history/index.html;
}
# The configuration of angular-history and react-history is the same as above
}Solution 2: Place the micro apps directly in the secondary directory, but set a special `activeRule`
└── html/ # root folder
|
├── vue-hash/ # the folder of the micro app `vue-hash`
├── vue-history/ # the folder of the micro app `vue-history`
├── react-hash/ # the folder of the micro app `react-hash`
├── react-history/ # the folder of the micro app `react-history`
├── angular-hash/ # the folder of the micro app `angular-hash`
├── angular-history/ # the folder of the micro app `angular-history`
├── index.html # index.html of the main app
├── css/ # the css folder of the main app
├── js/ # the js folder of the main appThe basic operation is the same as above, just make sure that activeRule is different from the storage path name of the micro app.
Scenario 2: The main app and micro apps are deployed on different servers and accessed through Nginx proxy
This is generally done because the main app is not allowed to access micro apps across domains. The practice is to forward all requests for a special path on the main app server to the micro app server, that is, a "micro app deployed on the main app server" effect is achieved through the proxy.
For example, the main app is on the A server, and the micro app is on the B server. The path /app1 is used to distinguish the micro app, that is, all requests starting with /app1 on the A server are forwarded to the B server.
the Nginx proxy configuration of the main app is:
/app1/ {
proxy_pass http://www.b.com/app1/;
proxy_set_header Host $host:$server_port;
}When the main app registers micro apps, entry can be a relative path, and activeRule cannot be the same as entry (otherwise the main app page refreshes and becomes a micro app):
registerMicroApps([
{
name: 'app1',
entry: '/app1/', // http://localhost:8080/app1/
container: '#container',
activeRule: '/child-app1',
},
],For micro apps bundled by webpack, the publicPath bundled by the micro app's webpack needs to be configured as /app1/, otherwise the micro app's index.html can be requested correctly, But the path of js/css in the micro app's index.html will not carry /app1/.
module.exports = {
output: {
publicPath: `/app1/`,
},
};After adding /app1/ to the publicPath of the micro app, it must be deployed in the /app1 directory, otherwise it cannot be accessed independently.
In addition, if you don't want the micro app to be accessed independently through the proxy path, you can judge based on some information requested. The requesting micro app in the main app is requested with fetch, which can include parameters and cookie. For example, judge by request header parameters:
if ($http_custom_referer != "main") {
rewrite /index /404.html;
}Upgrade from 1.x version to 2.x version
The micro apps does not need to be changed, and the main app needs to be adjusted.
The basic modification of registerMicroApps function is as follows:
- Remove the
renderparameter and only need to provide the container. - Add the
loaderparameter to display theloadingstatus. Originally, theloadingstatus was provided to therenderparameter. - The
activeRuleparameter can be abbreviated as/app, which is compatible with the previous function writing. - The
RegisterMicroAppsOptsparameter is removed and placed in the parameter of thestartfunction.
The basic modification of the start function is as follows:
- The
jsSandboxconfiguration has been removed and changed tosandbox, and the optional values have also been modified. - Added
getPublicPathandgetTemplateto replaceRegisterMicroAppsOpts.