## File: README.md

# qiankun（乾坤）

> [!WARNING]
>
> 🚧 qiankun 3.0 is currently under active development — check out the [Roadmap](https://github.com/umijs/qiankun/discussions/1378) for details. Looking for qiankun 2.x? Its documentation lives at [v2.qiankun.umijs.org](https://v2.qiankun.umijs.org).

> In Chinese, `qian(乾)` means heaven and `kun(坤)` earth. `qiankun` is the universe.

Qiankun enables you and your teams to build next-generation and enterprise-ready web applications leveraging [Micro Frontends](https://micro-frontends.org/). It is inspired by and based on [single-spa](https://github.com/single-spa/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](https://micro-frontends.org/)

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](https://github.com/single-spa/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 `window` and `document`, 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. Dynamic `import()` works, and so does a Vite dev server.
- 🛡 **Style isolation** — opt-in runtime scoping built on the CSS [`@scope`](https://developer.mozilla.org/docs/Web/CSS/@scope) at-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](packages/ui-bindings/react) and [Vue](packages/ui-bindings/vue), with loader and error-boundary slots, so a micro app is just another component.
- 🔧 **Bundler plugins** — [one plugin](packages/bundler-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/sandbox`](packages/sandbox) is usable on its own to contain any third-party script, with no micro-frontend framework attached.

## 🚀 Getting started

> [!NOTE]
>
> v3 ships under the `rc` tag while `latest` still points at 2.x. Install with an explicit `@rc` to 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:

```shell
npx skills add umijs/qiankun
```

Or add qiankun to an existing host application:

```shell
npm i qiankun@rc
```

Load a micro app into any container element, and keep the returned handle to unmount it:

```ts
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:

```ts
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](https://www.qiankunjs.com/guide/getting-started) for the bundler configuration each stack needs.

## 💿 Examples

Every example is deployed and browsable at **[examples.qiankunjs.com](https://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](https://examples.qiankunjs.com/standalone-sandbox/). 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:

```shell
git clone https://github.com/umijs/qiankun.git
cd qiankun
pnpm install
pnpm start:example
```

This 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](./examples/README.md) for what each app demonstrates.



## 📦 Packages

| Package | Version (click for changelogs) | Description |
| --- | :-- | --- |
| [qiankun](packages/qiankun) | [](packages/qiankun/CHANGELOG.md) | The framework: `registerMicroApps`, `loadMicroApp`, `start`, `prefetch` |
| [@qiankunjs/loader](packages/loader) | [](packages/loader/CHANGELOG.md) | Streaming HTML-entry loader |
| [@qiankunjs/sandbox](packages/sandbox) | [](packages/sandbox/CHANGELOG.md) | JS sandbox — usable standalone |
| [@qiankunjs/shared](packages/shared) | [](packages/shared/CHANGELOG.md) | Asset transpilers, fetch utilities, ESM-sandbox engine |
| [@qiankunjs/single-spa](packages/single-spa) | [](packages/single-spa/CHANGELOG.md) | Vendored [single-spa](https://github.com/single-spa/single-spa) fork the framework builds on |
| [@qiankunjs/react](packages/ui-bindings/react) | [](packages/ui-bindings/react/CHANGELOG.md) | `<MicroApp />` for React |
| [@qiankunjs/vue](packages/ui-bindings/vue) | [](packages/ui-bindings/vue/CHANGELOG.md) | `<MicroApp />` for Vue |
| [@qiankunjs/ui-shared](packages/ui-bindings/shared) | [](packages/ui-bindings/shared/CHANGELOG.md) | Shared internals of the UI bindings |
| [@qiankunjs/bundler-plugin](packages/bundler-plugin) | [](packages/bundler-plugin/CHANGELOG.md) | webpack 4/5 and Vite plugins for micro apps |

## 📖 Documentation

Full documentation lives at **[www.qiankunjs.com](https://www.qiankunjs.com)** (English and [简体中文](https://www.qiankunjs.com/zh-CN/)):

- [Quick start](https://www.qiankunjs.com/guide/getting-started) — a running micro-frontend in five minutes
- [Cookbook](https://www.qiankunjs.com/cookbook/) — style isolation, sandbox plugins, error handling, performance
- [API reference](https://www.qiankunjs.com/api/) — every option, typed
- [FAQ](https://www.qiankunjs.com/faq/) — the questions that come up most

Design decisions are recorded as RFCs under [docs/rfcs](./docs/rfcs).

## 🎯 Roadmap

qiankun 3.0 is under active development. The plan, and the discussion around it, is in the [3.0 Roadmap](https://github.com/umijs/qiankun/discussions/1378).

## 🤝 Contributing

[](https://deepwiki.com/umijs/qiankun)

Issues and pull requests are welcome. The repository is a pnpm monorepo that requires Node `^22.15 || >=24` and `pnpm@11`:

```shell
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 + prettier
```

Commits follow [Conventional Commits](https://www.conventionalcommits.org/) — releases and changelogs are derived from them, so do not hand-write changesets. [AGENTS.md](./AGENTS.md) documents the architecture, conventions and anti-patterns in depth.

## 👥 Contributors

Thanks to all the contributors!

<a href="https://github.com/umijs/qiankun/graphs/contributors">
  
</a>

## 🎁 Acknowledgements

- [single-spa](https://github.com/single-spa/single-spa) — what an awesome meta-framework for micro-frontends!
- [writable-dom](https://github.com/marko-js/writable-dom/) — utility to stream HTML content into a live document.

## 📄 License

Qiankun is [MIT licensed](./LICENSE).

---

## 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](/ecosystem/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](/guide/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:

```bash
mkdir qiankun-demo
cd qiankun-demo
npx skills add umijs/qiankun
```

Describe 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](/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

```bash [micro-app]
cd sub-app
npm install
npm run dev
# http://localhost:7101
```

```bash [main app]
cd 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](/ecosystem/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:

```tsx
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-plugin` prepares its HTML entry and development server for qiankun.
- Its entry module exports `bootstrap`, `mount`, and `unmount`. `mount` renders inside the container supplied by the main app, and `unmount` destroys 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`](/api/register-micro-apps) together with [`start`](/api/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](/tutorial/).
- Learn the application contract in [Lifecycle and props](/concepts/lifecycle-and-props).
- Prepare an existing [Vite](/cookbook/prepare-a-vite-app) or [Webpack](/cookbook/prepare-a-webpack-app) application.
- See every option and method in the [`loadMicroApp` API](/api/load-micro-app).

---

## File: docs/guide/getting-started.zh.md

---
toc: menu
---

# 快速上手

## 主应用

### 1. 安装 qiankun

```bash
$ yarn add qiankun # 或者 npm i qiankun -S
```

### 2. 在主应用中注册微应用

```ts
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 中，同时依次调用微应用暴露出的生命周期钩子。

如果微应用不是直接跟路由关联的时候，你也可以选择手动加载微应用的方式：

```ts
import { loadMicroApp } from 'qiankun';

loadMicroApp({
  name: 'app',
  entry: '//localhost:7100',
  container: '#yourContainer',
});
```

## 微应用

微应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。

### 1. 导出相应的生命周期钩子

微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出 `bootstrap`、`mount`、`unmount` 三个生命周期钩子，以供主应用在适当的时机调用。

```jsx
/**
 * 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，所以你可以在[这里](https://single-spa.js.org/docs/building-applications.html#registered-application-lifecycle)找到更多关于微应用生命周期相关的文档说明。

无 webpack 等构建工具的应用接入方式请见[这里](/zh/guide/tutorial#%E9%9D%9E-webpack-%E6%9E%84%E5%BB%BA%E7%9A%84%E5%BE%AE%E5%BA%94%E7%94%A8)

### 2. 配置微应用的打包工具

除了代码中暴露出相应的生命周期钩子之外，为了让主应用能正确识别微应用暴露出来的一些信息，微应用的打包工具需要增加如下配置：

#### webpack:
webpack v5:
```js
const packageName = require('./package.json').name;

module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    chunkLoadingGlobal: `webpackJsonp_${packageName}`,
  },
};
```

webpack v4:
```js
const packageName = require('./package.json').name;

module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    jsonpFunction: `webpackJsonp_${packageName}`,
  },
};
```

相关配置介绍可以查看 [webpack 相关文档](https://webpack.js.org/configuration/output/#outputlibrary)。

---

## File: docs/guide/README.md

---
nav:
  order: 0
toc: menu
---

# Introduction

Qiankun is an implementation of [Micro Frontends](https://micro-frontends.org/), which based on [single-spa](https://github.com/CanopyTax/single-spa). It aims to make it easier and painless to build a production-ready microfront-end architecture system.

Qiankun hatched from [Ant Financial](https://en.wikipedia.org/wiki/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](https://micro-frontends.org/)

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](https://www.youtube.com/watch?v=pU1gXA0rfwc) 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:

- [Micro Frontends](https://micro-frontends.org/)
- [Micro Frontends from martinfowler.com](https://martinfowler.com/articles/micro-frontends.html)

## 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](https://www.yuque.com/kuitos/gky7yw/gesexv)

## Features

- 📦 **Based On [single-spa](https://github.com/CanopyTax/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](https://github.com/umijs/plugins/tree/master/packages/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](https://github.com/CanopyTax/single-spa) 的[微前端](https://micro-frontends.org/)实现库，旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

qiankun 孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台，在经过一批线上应用的充分检验及打磨后，我们将其微前端内核抽取出来并开源，希望能同时帮助社区有类似需求的系统更方便的构建自己的微前端系统，同时也希望通过社区的帮助将 qiankun 打磨的更加成熟完善。

目前 qiankun 已在蚂蚁内部服务了超过 2000+ 线上应用，在易用性及完备性上，绝对是值得信赖的。

## 什么是微前端

> Techniques, strategies and recipes for building a **modern web app** with **multiple teams** that can **ship features independently**. -- [Micro Frontends](https://micro-frontends.org/)
>
> 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。

微前端架构具备以下几个核心价值：

- 技术栈无关  
  主框架不限制接入应用的技术栈，微应用具备完全自主权

- 独立开发、独立部署  
  微应用仓库独立，前后端可独立开发，部署完成后主框架自动完成同步更新

- 增量升级

  在面对各种复杂场景时，我们通常很难对一个已经存在的系统做全量的技术栈升级或重构，而微前端是一种非常好的实施渐进式重构的手段和策略

- 独立运行时  
  每个微应用之间状态隔离，运行时状态不共享

微前端架构旨在解决单体应用在一个相对长的时间跨度下，由于参与的人员、团队的增多、变迁，从一个普通应用演变成一个巨石应用([Frontend Monolith](https://www.youtube.com/watch?v=pU1gXA0rfwc))后，随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。

更多关于微前端的相关介绍，推荐大家可以去看这几篇文章：

- [Micro Frontends](https://micro-frontends.org/)
- [Micro Frontends from martinfowler.com](https://martinfowler.com/articles/micro-frontends.html)
- [可能是你见过最完善的微前端解决方案](https://zhuanlan.zhihu.com/p/78362028)
- [微前端的核心价值](https://zhuanlan.zhihu.com/p/95085796)

## qiankun 的核心设计理念

- 🥄 简单

  由于主应用微应用都能做到技术栈无关，qiankun 对于用户而言只是一个类似 jQuery 的库，你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。同时由于 qiankun 的 HTML entry 及沙箱的设计，使得微应用的接入像使用 iframe 一样简单。

- 🍡 解耦/技术栈无关

  微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用，而 qiankun 的诸多设计均是秉持这一原则，如 HTML entry、沙箱、应用间通信等。这样才能确保微应用真正具备 独立开发、独立运行 的能力。

## 它是如何工作的

TODO

## 为什么不是 iframe

看这里 [Why Not Iframe](https://www.yuque.com/kuitos/gky7yw/gesexv)

## 特性

- 📦 **基于 [single-spa](https://github.com/CanopyTax/single-spa)** 封装，提供了更加开箱即用的 API。
- 📱 **技术栈无关**，任意技术栈的应用均可 使用/接入，不论是 React/Vue/Angular/JQuery 还是其他等框架。
- 💪 **HTML Entry 接入方式**，让你接入微应用像使用 iframe 一样简单。
- 🛡​ **样式隔离**，确保微应用之间样式互相不干扰。
- 🧳 **JS 沙箱**，确保微应用之间 全局变量/事件 不冲突。
- ⚡️ **资源预加载**，在浏览器空闲时间预加载未打开的微应用资源，加速微应用打开速度。
- 🔌 **umi 插件**，提供了 [@umijs/plugin-qiankun](https://github.com/umijs/plugins/tree/master/packages/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:

1. check you have exported the specified lifecycles, see the [doc](/guide/getting-started#1-exports-lifecycles-from-sub-app-entry)

2. check you have set the specified configuration with your bundler, see the [doc](/guide/getting-started#2-config-sub-app-bundler)

3. Check the webpack of micro app whether it configured with `output.globalObject` or not, be sure its value was `window` if it had, or remove it to use default value.

4. Check your `package.json` name field is unique between sub apps.

5. 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 `entry` in 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>
   ```

6. If the development environment is OK but the production environment is not, check whether the `index.html` and `entry js` of the micro app are returned normally, for example, `404.html` is returned.

7. If you're using webpack5 and not using module federation, please see [here](https://github.com/umijs/qiankun/issues/1092#issuecomment-1109673224)

8. 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.

```js
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());
```

9. 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 returns `true`，that it is caused by this reason, and there are mainly the following two solutions:

   - Solution 1: Modify the `libraryTarget` of the micro-app `webpack` to `'window'`.

   ```diff
   const 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 the `window` in the entry file, refer to[Micro app built without webpack](/guide/tutorial#micro-app-built-without-webpack).

10. If it still not works after the steps above, this is usually due to browser compatibility issues. Try to **set the webpack `output.library` of the broken sub app the same with your main app registration for your app**, such as:

Such as here is the main configuration:

```ts {4}
// main app
registerMicroApps([
  {
    name: 'brokenSubApp',
    entry: '//localhost:7100',
    container: '#yourContainer',
    activeRule: '/react',
  },
]);
```

Set the `output.library` the same with main app registration:

```js {4}
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:

1. The root id of the micro app conflicts with other DOM, and the solution is to modify the search range of the root id.

   `vue` micro app:

   ```js
   function 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` micro app：

   ```js
   function 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'));
   }
   ```

2. 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 `ignore` attribute 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:

1. Based on the specific exception message, check if the js file that errors has syntax errors, like missing semicolons, depending on uninitialized variables etc.
2. Whether it depends on global variables provided by the main app, but the main app did not initialize them.
3. 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:

1. When the main app registers this route, add a `*` to `path`, **Note: If this route has other sub-routes, you need to register another route, just use this component**.
   ```js
   const routes = [
     {
       path: '/portal/*',
       name: 'portal',
       component: () => import('../views/Portal.vue'),
     },
   ];
   ```
2. The `activeRule` of the micro app needs to include the route `path` of the main app.
   ```js
   registerMicroApps([
     {
       name: 'app1',
       entry: 'http://localhost:8080',
       container: '#container',
       activeRule: '/portal/app1',
     },
   ]);
   ```
3. Call the `start` function in the `mounted` cycle of the `Portal.vue` component, **be careful not to call it repeatedly**.
   ```js
   import { 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：

1. The main app registers a wildcard sub route for this route, and the content is empty.

   ```ts
   const routes: Routes = [
     {
       path: 'portal',
       component: PortalComponent,
       children: [{ path: '**', component: EmptyComponent }],
     },
   ];
   ```

2. The `activeRule` of the micro app needs to include the route `path` of the main app.
   ```js
   registerMicroApps([
     {
       name: 'app1',
       entry: 'http://localhost:8080',
       container: '#container',
       activeRule: '/portal/app1',
     },
   ]);
   ```
3. Call the `start` function in the `ngAfterViewInit` cycle of this routing component, **be careful not to call it repeatedly**.
   ```ts
   import { 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`.

```javascript
if (inBrowser && window.Vue) {
  window.Vue.use(VueRouter);
}
```

To solve the error, choose one of the options listed below:

1. Use bundler to pack `Vue` library, instead of CDN or external module
2. Rename `Vue` to 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:

```js
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
```

For more details, check the [webpack doc](https://webpack.js.org/guides/public-path/#on-the-fly).

<Alert type="info">
Runtime publicPath addresses the problem of incorrect scripts, styles, images, and other addresses for dynamically loaded in sub application.
</Alert>

### 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:

```js
{
  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:

1. Upload all static resources such as pictures to `cdn`, and directly reference the address of `cdn` in `css` (**recommended**)

2. Use the `url-loader` of `webpack` to package font files and images as `base64` (suitable for projects with small font files and images)(**recommended**)

```js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|webp|woff2?|eot|ttf|otf)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {},
          },
        ],
      },
    ],
  },
};
```

`vue-cli3` project:

```js
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`:

```js
module.exports = {
  chainWebpack: (config) => {
    config.module.rule('fonts').type('asset/inline').set('generator', {});
    config.module.rule('images').type('asset/inline').set('generator', {});
  },
};
```

3. Use the `file-loader` of `webpack` to inject the full path when packaging it (suitable for projects with large font files and images)

```js
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:

```js
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();
  },
};
```

4. Combine the two schemes, convert small files to `base64`, and inject path prefixes for large files

```js
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：

```js
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,
          },
        },
      });
  },
};
```

5. The `vue-cli3` project can package `css` into `js` without generating files separately (not recommended, only suitable for projects with less `css`)

Configuration reference [vue-cli3 official website](https://cli.vuejs.org/zh/config/#css-extract):

```js
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](https://developer.mozilla.org/en-US/docs/Web/HTTP/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](https://enable-cors.org/server_nginx.html).

## 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:

```html
<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.

```js
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.

```js
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:

1. Copy an `index.txt` file from `index.html` when the micro-app is published.

2. Change `entry` in the main app to a txt address, for example:

   ```diff
   registerMicroApps(
     [
   -    { 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](https://ant.design), you can follow [this doc](https://ant.design/docs/react/customize-theme) to make it works.

Example for antd：

1. use webpack to modify antd less variable

   ```diff
   {
     loader: 'less-loader',
   + options: {
   +   modifyVars: {
   +     '@ant-prefix': 'yourPrefix',
   +   },
   +   javascriptEnabled: true,
   + },
   }
   ```

2. set antd [ConfigProvider](https://ant.design/components/config-provider-cn/)

   ```jsx
   import { ConfigProvider } from 'antd';

   export const MyApp = () => (
     <ConfigProvider prefixCls="yourPrefix">
       <App />
     </ConfigProvider>
   );
   ```

Detailed documentation pls check [antd official guide](https://ant.design/docs/react/customize-theme).

<Alert type="info">
In the latest version, you can also try to config ` {sandbox: {experimentalStyleIsolation: true}} ` to open the runtime scoped CSS feature, thus solving the problem of the style of the isolation between applications.
PS: Currently, this feature is still in the experimental stage, if encounter some problems please submit an [issue](https://github.com/umijs/qiankun/issues/new?assignees=&labels=&template=bug_report.md&title=) to help us improve together.
</Alert>

## How to make sub app to run independently?

Use the builtin global variable to identify the environment which provided by qiankun master:

```js
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`:

```js {2,3,7}
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`.

<Alert>
Notice that no more than one application that relies on router can be displayed on the page at the same time, as the browser has only one url location, if there is more than one routing apps, it will definitely result in one of them to be 404 found.
</Alert>

## How to extract the common library？

> Don’t share a runtime, even if all teams use the same framework. - [Micro Frontends](https://micro-frontends.org/)

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](/api#startopts).

### 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**:

<Alert type="info">
What's [polyfill](https://developer.mozilla.org/en-US/docs/Glossary/Polyfill)
</Alert>

```javascript
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](https://babeljs.io/docs/en/babel-preset-env).**

<Alert type="info">
You can also check out[this article](https://www.yuque.com/kuitos/gky7yw/qskte2)to learn more about IE compatibility.
</Alert>

## 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](https://caniuse.com/#search=fetch), you should get the [polyfill](https://github.com/github/fetch) 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:

- [HTML5 History Mode](https://router.vuejs.org/guide/essentials/history-mode.html)
- [browserRouter](https://reactrouter.com/web/api/BrowserRouter)

## 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:

```js
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:

1. `history. PushState ()` : [MDN usage introduction] (https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState)
2. Direct use of native a full address label, such as: `< a href = "http://localhost:8080/app1" > app1 < / a >`
3. 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 **):

```js
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:

```js
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:

```diff
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:

```diff
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](/api#registermicroappsapps-lifecycles), you need to configure a custom fetch in the start method, such as:

  ```js
  import { 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](/api#loadmicroappapp-configuration), you need to configure a custom fetch when invoking, such as:

  ```js
  import { 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](https://umijs.org/zh-CN/plugins/plugin-qiankun) to use qiankun，then you only need to enable the credentials configuration for the corresponding microapp:

  ```diff
  export 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:

```js
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 中识别出其导出的生命周期钩子。

可以通过以下几个步骤解决这个问题：

1. 检查微应用是否已经导出相应的生命周期钩子，参考[文档](/zh/guide/getting-started#1-导出相应的生命周期钩子)。

2. 检查微应用的 webpack 是否增加了指定的配置，参考[文档](/zh/guide/getting-started#2-配置微应用的打包工具)。

3. 检查微应用的 webpack 是否配置了 `output.globalObject` 的值，如果有请确保其值为 `window`，或者移除该配置从而使用默认值。

4. 检查微应用的 `package.json` 中的 `name` 字段是否是微应用中唯一的。

5. 检查微应用的 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>
   ```

6. 如果开发环境可以，生产环境不行，检查微应用的 `index.html` 和 `entry js` 是否正常返回，比如说返回了 `404.html`。

7. 如果你正在使用 webpack5，但没用使用模块联邦，请看[这个 issues](https://github.com/umijs/qiankun/issues/1092#issuecomment-1109673224)。

8. 如果你正在使用 webpack5，并且使用了使用模块联邦。需要在 index 文件中暴露生命周期函数，然后在 bootstrap 文件向外暴露生命周期函数。

```js
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());
```

9.  检查主应用和微应用是否使用了 AMD 或 CommonJS 模块化。检查方法：单独运行微应用和主应用，在控制台输入如下代码：`(typeof exports === 'object' && typeof module === 'object') || (typeof define === 'function' && define.amd) || typeof exports === 'object'`，如果返回 `true`，则说明是这种情况，主要有以下两个解决办法：

    - 解决办法 1：修改微应用 `webpack` 的 `libraryTarget` 为 `'window'` 。

    ```diff
    const packageName = require('./package.json').name;
    module.exports = {
      output: {
        library: `${packageName}-[name]`,
    -    libraryTarget: 'umd',
    +    libraryTarget: 'window',
        jsonpFunction: `webpackJsonp_${packageName}`,
      },
    };
    ```

    - 解决办法 2：微应用不打包成 umd ，直接在入口文件把生命周期函数挂载到 window 上，参考[非 webpack 构建的微应用](/zh/guide/tutorial#非-webpack-构建的微应用)。

10. 如果在上述步骤完成后仍有问题，通常说明是浏览器兼容性问题导致的。可以尝试 **将有问题的微应用的 webpack `output.library` 配置成跟主应用中注册的 `name` 字段一致**，如：

假如主应用配置是这样的：

```ts {4}
// 主应用
registerMicroApps([
  {
    name: 'brokenSubApp',
    entry: '//localhost:7100',
    container: '#yourContainer',
    activeRule: '/react',
  },
]);
```

将微应用的 `output.library` 改为跟主应用中注册的一致：

```js {4}
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 节点不存在了。可能的原因有：

1. 微应用的根 `id` 与其他 DOM 冲突。解决办法是：修改根 `id` 的查找范围。

   `vue` 微应用：

   ```js
   function 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` 微应用：

   ```js
   function 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'));
   }
   ```

2. 微应用的某些 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`。**

子应用本身的异常，可以尝试通过以下步骤排查解决：

1. 根据具体的异常信息，检查报错的 js 是否有语法错误，比如少了分号、依赖了未初始化的变量等。
2. 是否依赖了主应用提供的全局变量，但实际主应用并未初始化。
3. 兼容性问题。子应用这个 js 本身在当前运行环境存在语法兼容性问题。

## 如何在主应用的某个路由页面加载微应用

必须保证微应用加载时主应用这个路由页面也加载了。

`vue` + `vue-router` 技术栈的主应用：

1. 主应用注册这个路由时给 `path` 加一个 `*`，**注意：如果这个路由有其他子路由，需要另外注册一个路由，仍然使用这个组件即可**。
   ```js
   const routes = [
     {
       path: '/portal/*',
       name: 'portal',
       component: () => import('../views/Portal.vue'),
     },
   ];
   ```
2. 微应用的 `activeRule` 需要包含主应用的这个路由 `path`。
   ```js
   registerMicroApps([
     {
       name: 'app1',
       entry: 'http://localhost:8080',
       container: '#container',
       activeRule: '/portal/app1',
     },
   ]);
   ```
3. 在 `Portal.vue` 这个组件的 `mounted` 周期调用 `start` 函数，**注意不要重复调用**。
   ```js
   import { start } from 'qiankun';
   export default {
     mounted() {
       if (!window.qiankunStarted) {
         window.qiankunStarted = true;
         start();
       }
     },
   };
   ```

`react` + `react-router` 技术栈的主应用：只需要让微应用的 `activeRule` 包含主应用的这个路由即可。

`angular` + `angular-router` 技术栈的主应用，与 vue 项目类似：

1. 主应用给这个路由注册一个通配符的子路由，内容为空。

   ```ts
   const routes: Routes = [
     {
       path: 'portal',
       component: PortalComponent,
       children: [{ path: '**', component: EmptyComponent }],
     },
   ];
   ```

2. 微应用的 `activeRule` 需要包含主应用的这个路由 `path`。
   ```js
   registerMicroApps([
     {
       name: 'app1',
       entry: 'http://localhost:8080',
       container: '#container',
       activeRule: '/portal/app1',
     },
   ]);
   ```
3. 在这个路由组件的 `ngAfterViewInit` 周期调用 `start` 函数，**注意不要重复调用**。
   ```ts
   import { 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` 对象上了。

```javascript
if (inBrowser && window.Vue) {
  window.Vue.use(VueRouter);
}
```

可以从以下方式中选择一种来解决问题：

1. 在主应用中不使用 CDN 等 external 的方式来加载 `Vue` 框架，使用前端打包软件来加载模块
2. 在主应用中，将 `window.Vue` 变量改个名称，例如 `window.Vue2 = window.Vue; delete window.Vue`

## 为什么微应用加载的资源会 404？

原因是 webpack 加载资源时未使用正确的 `publicPath`。

可以通过以下两个方式解决这个问题：

### a. 使用 webpack 运行时 publicPath 配置

qiankun 将会在微应用 bootstrap 之前注入一个运行时的 publicPath 变量，你需要做的是在微应用的 entry js 的顶部添加如下代码：

```js
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
```

关于运行时 publicPath 的技术细节，可以参考 [webpack 文档](https://webpack.js.org/guides/public-path/#on-the-fly)。

<Alert type="info">
runtime publicPath 主要解决的是微应用动态载入的 脚本、样式、图片 等地址不正确的问题。
</Alert>

### b. 使用 webpack 静态 publicPath 配置

你需要将你的 webpack `publicPath` 配置设置成一个绝对地址的 url，比如在开发环境可能是：

```js
{
  output: {
    publicPath: `//localhost:${port}`,
  }
}
```

### 微应用打包之后 css 中的字体文件和图片加载 404

原因是 `qiankun` 将外链样式改成了内联样式，但是字体文件和背景图片的加载路径是相对路径。

而 `css` 文件一旦打包完成，就无法通过动态修改 `publicPath` 来修正其中的字体文件和背景图片的路径。

主要有以下几个解决方案：

1. 所有图片等静态资源上传至 `cdn`，`css` 中直接引用 `cdn` 地址（**推荐**）

2. 借助 `webpack` 的 `url-loader` 将字体文件和图片打包成 `base64`（适用于字体文件和图片体积小的项目）（**推荐**）

```js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|webp|woff2?|eot|ttf|otf)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {},
          },
        ],
      },
    ],
  },
};
```

`vue-cli3` 项目写法：

```js
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`，写法：

```js
module.exports = {
  chainWebpack: (config) => {
    config.module.rule('fonts').type('asset/inline').set('generator', {});
    config.module.rule('images').type('asset/inline').set('generator', {});
  },
};
```

3. 借助 `webpack` 的 `file-loader` ，在打包时给其注入完整路径（适用于字体文件和图片体积比较大的项目）

```js
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` 项目写法：

```js
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();
  },
};
```

4. 将两种方案结合起来，小文件转 `base64` ，大文件注入路径前缀

```js
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` 项目写法：

```js
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,
          },
        },
      });
  },
};
```

5. `vue-cli3` 项目可以将 `css` 打包到 `js`里面，不单独生成文件(不推荐，仅适用于 `css` 较少的项目)

配置参考 [vue-cli3 官网](https://cli.vuejs.org/zh/config/#css-extract):

```js
module.exports = {
  css: {
    extract: false,
  },
};
```

## 微应用静态资源一定要支持跨域吗？

是的。

由于 qiankun 是通过 fetch 去获取微应用的引入的静态资源的，所以必须要求这些静态资源支持[跨域](https://developer.mozilla.org/zh/docs/Web/HTTP/Access_control_CORS)。

如果是自己的脚本，可以通过开发服务端跨域来支持。如果是三方脚本且无法为其添加跨域头，可以将脚本拖到本地，由自己的服务器 serve 来支持跨域。

参考：[Nginx 跨域配置](https://segmentfault.com/a/1190000012550346)

## 如何解决由于运营商动态插入的脚本加载异常导致微应用加载失败的问题

运营商插入的脚本通常会用 async 标记从而避免 block 微应用的加载，这种通常没问题，如：

```html
<script async src="//www.rogue.com/rogue.js"></script>
```

但如果有些插入的脚本不是被标记成 async 的，这类脚本一旦运行失败，将会导致整个应用被 block 且后续的脚本也不再执行。我们可以通过以下几个方式来解决这个问题：

### 使用自定义的 getTemplate 方法

通过自己实现的 getTemplate 方法过滤微应用 HTML 模板中的异常脚本

```js
import { start } from 'qiankun';

start({
  getTemplate(tpl) {
    return tpl.replace('<script src="/to-be-replaced.js"><script>', '');
  },
});
```

### 使用自定义的 fetch 方法

通过自己实现的 fetch 方法拦截有问题的脚本

```js
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，也有一个更简单高效的方案：

1. 微应用发布时从 index.html 复制出一个 index.txt 文件出来

2. 将主应用中的 entry 改为 txt 地址，如：

   ```diff
   registerMicroApps(
     [
   -    { name: 'app1', entry: '//localhost:8080/index.html', container, activeRule },
   +    { name: 'app1', entry: '//localhost:8080/index.txt', container, activeRule },
     ],
   );
   ```

## 如何确保主应用跟微应用之间的样式隔离

qiankun 将会自动隔离微应用之间的样式（开启沙箱的情况下），你可以通过手动的方式确保主应用与微应用之间的样式隔离。比如给主应用的所有样式添加一个前缀，或者假如你使用了 [ant-design](https://ant.design) 这样的组件库，你可以通过[这篇文档](https://ant.design/docs/react/customize-theme)中的配置方式给主应用样式自动添加指定的前缀。

以 antd 为例：

1. 配置 webpack 修改 less 变量

   ```diff
   {
     loader: 'less-loader',
   + options: {
   +   modifyVars: {
   +     '@ant-prefix': 'yourPrefix',
   +   },
   +   javascriptEnabled: true,
   + },
   }
   ```

2. 配置 antd [ConfigProvider](https://ant.design/components/config-provider-cn/)

   ```jsx
   import { ConfigProvider } from 'antd';

   export const MyApp = () => (
     <ConfigProvider prefixCls="yourPrefix">
       <App />
     </ConfigProvider>
   );
   ```

详细文档参考 [antd 官方指南](https://ant.design/docs/react/customize-theme)。

<Alert type="info">
在最新的 qiankun 版本中，你也可以尝试通过配置 `{ sandbox : { experimentalStyleIsolation: true } }` 的方式开启运行时的 scoped css 功能，从而解决应用间的样式隔离问题。
PS：目前该特性还处于实验阶段，如果碰到一些问题欢迎提 [issue](https://github.com/umijs/qiankun/issues/new?assignees=&labels=&template=bug_report_cn.md&title=%5BBug%5D%E8%AF%B7%E9%81%B5%E5%BE%AA%E4%B8%8B%E6%96%87%E6%A8%A1%E6%9D%BF%E6%8F%90%E4%BA%A4%E9%97%AE%E9%A2%98%EF%BC%8C%E5%90%A6%E5%88%99%E6%82%A8%E7%9A%84%E9%97%AE%E9%A2%98%E4%BC%9A%E8%A2%AB%E5%85%B3%E9%97%AD) 来帮助我们一起改善。
</Alert>

## 如何独立运行微应用？

有些时候我们希望直接启动微应用从而更方便的开发调试，你可以使用这个全局变量来区分当前是否运行在 qiankun 的主应用的上下文中：

```ts
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export const mount = async () => render();
```

## 如何同时激活两个微应用？

微应用何时被激活完全取决于你的 `activeRule` 配置，比如下面的例子里，我们将 `reactApp` 和 `react15App` 的 `activeRule` 逻辑设置成一致的：

```js {2,3,7}
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。

<Alert>
页面上不能同时显示多个依赖于路由的微应用，因为浏览器只有一个 url，如果有多个依赖路由的微应用同时被激活，那么必定会导致其中一个 404。
</Alert>

## 如何提取出公共的依赖库？

> 不要共享运行时，即便所有的团队都是用同一个框架。- [微前端](https://micro-frontends.org/)

虽然共享依赖并不建议，但如果你真的有这个需求，你可以在微应用中将公共依赖配置成 `external`，然后在主应用中导入这些公共依赖。

qiankun 2.0 版本将提供一种更智能的方式使其自动化。

## qiankun 能兼容 ie 吗？

> 兼容.

但是 IE 环境下（不支持 Proxy 的浏览器）只能使用单实例模式，qiankun 会自动将 `singular` 配置为 `true`。

你可以在[这里](/zh/api#startopts)找到 singular 相关说明。

### 如何给 ie 打补丁？

如果希望 qiankun （或其依赖库、或者您的应用本身）在 IE 下正常运行，你**至少**需要在应用入口引入以下这些 polyfills：

<Alert type="info">
什么是 [polyfill](https://developer.mozilla.org/zh-CN/docs/Glossary/Polyfill)
</Alert>

```javascript
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 官方文档](https://babeljs.io/docs/en/babel-preset-env) 找到。**

<Alert type="info">
您也可以查看[这篇文章](https://www.yuque.com/kuitos/gky7yw/qskte2)来获取更多 IE 兼容相关的知识。
</Alert>

## 报错 `Here is no "fetch" on the window env, you need to polyfill it`

qiankun 依赖的 import-html-entry 通过 `window.fetch` 来获取微应用的资源， 部分[不支持 fetch 的浏览器](https://caniuse.com/#search=fetch)需要在入口处打上相应的 [polyfill](https://github.com/github/fetch)

## 微应用 JSONP 跨域错误怎么处理？

qiankun 会将微应用的动态 script 加载（例如 JSONP）转化为 fetch 请求，因此需要相应的后端服务支持跨域，否则会导致错误。

在单实例模式下，你可以使用 `excludeAssetFilter` 参数来放行这部分资源请求，但是注意，被该选项放行的资源会逃逸出沙箱，由此带来的副作用需要你自行处理。

若在多实例模式下使用 JSONP，单纯使用 `excludeAssetFilter` 并不能取得好的效果，因为各应用被沙箱所隔离；你可以在主应用提供统一的 JSONP 工具，微应用调用主应用提供的该工具来曲线救国。

## 微应用路径下刷新后 404？

通常是因为你使用的是 browser 模式的路由，这种路由模式的开启需要服务端配合才行。具体配置方式参考：

- [HTML5 History 模式](https://router.vuejs.org/zh/guide/essentials/history-mode.html)
- [browserHistory](https://react-guide.github.io/react-router-cn/docs/guides/basics/Histories.html#browserHistory)

## 主应用如何配置 404 页面？

首先不应该写通配符 `*` ，可以将 404 页面注册为一个普通路由页面，比如说 `/404` ，然后在主应用的路由钩子函数里面判断一下，如果既不是主应用路由，也不是微应用，就跳转到 404 页面。

以`vue-router`为例，伪代码如下：

```js
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`。有这几种办法可以跳转：

1. `history.pushState()`：[mdn 用法介绍](https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState)
2. 直接使用原生 a 标签写完整地址，如：`[app1](http://localhost:8080/app1)`
3. 修改 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 的方式加载微应用（**不推荐**）：

```js
loadMicroApp({
  name: 'configEntry',
  entry: {
    scripts: ['//t.com/t.js'],
    styles: ['//t.com/t.css'],
  },
});
```

微应用的 entry js 由于没有附属的 html，mount 钩子直接这么写的：

```js
export async function mount(props) {
  ReactDOM.render(<App />, props.container);
}
```

因为 `props.container` 并不是一个空的容器，里面会包含微应用通过 styles 配置注册进来的样式表等信息，所以当我们直接以`props.container` 为 react 应用的容器渲染时，会把容器里原来的所有 dom 结构全部覆盖掉，从而导致样式表丢失。

我们需要给使用 config entry 的微应用构造一个空的渲染容器，专门用来挂载 react 应用：

```diff
loadMicroApp({
  name: 'configEntry',
  entry: {
+   html: '',
    scripts: ['//t.com/t.js'],
    styles: ['//t.com/t.css']
  }
});
```

mount 钩子里不是直接渲染到 `props.container` ，而是渲染到其 `root` 节点里：

```diff
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](/zh/api#registermicroappsapps-lifecycles) 加载微应用的，你需要在 start 方法里配置自定义 fetch，如：

  ```js
  import { 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](/zh/api#loadmicroappapp-configuration) 加载微应用的，你需要在调用时配置自定义 fetch，如：

  ```js
  import { 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](https://umijs.org/zh-CN/plugins/plugin-qiankun) 来使用 qiankun 的，那么你只需要给对应的微应用开启 credentials 配置即可：

  ```diff
  export default {
    qiankun: {
      master: {
        apps: [
          {
            name: 'app',
            entry: '//app.alipay.com/entry.html',
  +         credentials: true,
          }
        ]
      }
    }
  }
  ```

## 如何解决子应用给 window 对象添加事件处理函数不生效的问题

由于子应用访问的 window 对象是被 qiankun 代理后的对象，因此直接给 window 对象添加事件处理函数是无效的，可以通过 addEventListener 给 window 添加事件监听器来解决该问题：

```js
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:

```js
registerMicroApps([
  {
    name: 'app',
    entry: 'http://localhost:8080',
    container: '#container',
    activeRule: '/app',
  },
]);
```

1. When the micro app is in `history` mode, just set the route `base`.

2. When the micro app is in the `hash` mode, the performance of the three routes is inconsistent

   | routing        | main app jump `/app/#/about`   | special configuration     |
   | -------------- | ------------------------------ | ------------------------- |
   | vue-router     | Response `about` routing       | none                      |
   | react-router   | not responding `about` routing | none                      |
   | angular-router | Response `about` routing       | need to set `--base-href` |

   `Angular` app set `--base-href` in `package.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 `angular` micro 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.js` to:

     ```js
     __webpack_public_path__ = window.__POWERED_BY_QIANKUN__
       ? window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
       : `http://${ip}:${port}/`; // Fill in your actual deployment address
     ```

   - Solution 2: Modify the bundling command and deploy the micro app in the `angular9` directory:

     ```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:

```js
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:

```js
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:

1. You must configure the `publicPath` when building `webpack` as the **directory name**. For more information, please see [webpack official instructions](https://webpack.js.org/configuration/output/#outputpublicpath) 和 [vue-cli3 official instructions](https://cli.vuejs.org/config/#publicpath).

2. The micro app of the `history` route needs to set the `base`, the value is **directory name**, which is used when the micro app is accessed independently.

Pay attention to three points after deployment:

1. `activeRule` cannot 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.
2. The real access path of the micro app is the `entry` of the micro app, and the `entry` can be a relative path.
3. The `/` at the end of the `entry` path of the micro app cannot be omitted, otherwise the `publicPath` will be set incorrectly. For example, if the access path of the child item is `http://localhost:8080/app1`, then `entry` It is `http://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 app
```

At 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-history` micro app

  Routing's base configuration:

  ```js
  base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/child/vue-history/',
  ```

  Webpack's publicPath configuration（`vue.config.js`）:

  ```js
  module.exports = {
    publicPath: '/child/vue-history/',
  };
  ```

- `react-history` micro app

  Routing's base configuration:

  ```html
  
  ```

  Webpack's publicPath configuration:

  ```js
  module.exports = {
    output: {
      publicPath: '/child/react-history/',
    },
  };
  ```

- `angular-history` micro app

  Routing's base configuration:

  ```js
  providers: [
    {
      provide: APP_BASE_HREF,
      useValue: window.__POWERED_BY_QIANKUN__ ? '/app-angular/' : '/child/angular-history/',
    },
  ];
  ```

  The `publicPath` of webpack is set by `deploy-url`, modify `package.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):

```js
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:

```conf
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 app
```

The 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):

```js
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/`.

```js
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:

```js
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:

1. Remove the `render` parameter and only need to provide the container.
2. Add the `loader` parameter to display the `loading` status. Originally, the `loading` status was provided to the `render` parameter.
3. The `activeRule` parameter can be abbreviated as `/app`, which is compatible with the previous function writing.
4. The `RegisterMicroAppsOpts` parameter is removed and placed in the parameter of the `start` function.

The basic modification of the `start` function is as follows:

1. The `jsSandbox` configuration has been removed and changed to `sandbox`, and the optional values have also been modified.
2. Added `getPublicPath` and `getTemplate` to replace `RegisterMicroAppsOpts`.