---
toc: menu
---
# project practise
This tutorial is suitable for people who are new to `qiankun`, and introduces how to build a `qiankun` project from 0.
## main app
The main app is not limited to the technical framework, it only needs to provide a container DOM, then register the micro apps and start it.
Install `qiankun` first :
```shell
$ yarn add qiankun # or npm i qiankun -S
```
Register the micro apps and start:
```js
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'angularApp',
entry: '//localhost:4200',
container: '#container',
activeRule: '/app-angular',
},
{
name: 'reactApp',
entry: '//localhost:3000',
container: '#container',
activeRule: '/app-react',
},
{
name: 'vueApp',
entry: '//localhost:8080',
container: '#container',
activeRule: '/app-vue',
},
]);
// start qiankun
start();
```
## micro app
Micro apps are divided into projects with `webpack` and without `webpack`. The things that need to be done for micro apps with `webpack` (mainly refers to Vue, React, Angular) are:
1. Added `public-path.js` file, used to modify the runtime `publicPath`.[What is publicPath at runtime?](https://webpack.js.org/guides/public-path/#on-the-fly).
Note: `publicPath` at runtime and `publicPath` at build time are different, and the two cannot be equivalently substituted.
2. It is recommended to use the route of the `history` mode for the micro app. The route `base` needs to be set, and the value is the same as its `activeRule`.
3. Import `public-path.js` at the top of the entry file, modify and export three `lifecycles` functions.
4. Modify the `webpack` configuration to allow cross-domain in development environments and bundle with `umd`.
The main modifications are the above four, which may change according to different situations of the project. For example, if your project is deployed separately from all other files of `index.html`, it means that you have set the `publicPath` at build time to the full path, so you don’t need to modify the `publicPath` at runtime (the first step can be omitted).
For micro app built without `webpack`, just mount `lifecycles` to `window`.
### React micro app
Take the `react 16` project generated by `create react app` as an example, with `react-router-dom` 5.x.
1. Add `public-path.js` in the `src` directory:
```js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
2. Set the `base` of `history` mode routing:
```html
```
3. The entry file `index.js` is modified. In order to avoid the root id `#root` from conflicting with other DOMs, the search scope needs to be limited.
```js
import './public-path';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
function render(props) {
const { container } = props;
ReactDOM.render(, container ? container.querySelector('#root') : document.querySelector('#root'));
}
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function bootstrap() {
console.log('[react16] react app bootstraped');
}
export async function mount(props) {
console.log('[react16] props from main framework', props);
render(props);
}
export async function unmount(props) {
const { container } = props;
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}
```
4. Modify `webpack` configuration
Install the plugin `@rescripts/cli`, of course, you can also choose other plugins, such as `react-app-rewired`.
```dash
npm i -D @rescripts/cli
```
Add `.rescriptsrc.js` to the root directory:
```js
const { name } = require('./package');
module.exports = {
webpack: config => {
config.output.library = `${name}-[name]`;
config.output.libraryTarget = 'umd';
config.output.jsonpFunction = `webpackJsonp_${name}`;
config.output.globalObject = 'window';
return config;
},
devServer: _ => {
const config = _;
config.headers = {
'Access-Control-Allow-Origin': '*',
};
config.historyApiFallback = true;
config.hot = false;
config.watchContentBase = false;
config.liveReload = false;
return config;
},
};
```
Modify `package.json`:
```diff
- "start": "react-scripts start",
+ "start": "rescripts start",
- "build": "react-scripts build",
+ "build": "rescripts build",
- "test": "react-scripts test",
+ "test": "rescripts test",
- "eject": "react-scripts eject"
```
### Vue micro app
Take the `vue 2.x` project generated by `vue-cli 3+` as an example, and add it after the `vue 3` version becomes stable.
1. Add `public-path.js` in the `src` directory:
```js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
3. The entry file `main.js` is modified. In order to avoid the root id `#app` from conflicting with other DOMs, the search scope needs to be limited.
```js
import './public-path';
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import routes from './router';
import store from './store';
Vue.config.productionTip = false;
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/',
mode: 'history',
routes,
});
instance = new Vue({
router,
store,
render: h => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// when run independently
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
console.log('[vue] props from main framework', props);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}
```
3. Modify `webpack` configuration(`vue.config.js`):
```js
const { name } = require('./package');
module.exports = {
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',// bundle the micro app into umd library format
jsonpFunction: `webpackJsonp_${name}`,
},
},
};
```
### Angular micro app
Take the `angular 9` project generated by `Angular-cli 9` as an example, other versions of `angular` will be added later.
1. Add the file `public-path.js` in the `src` directory with the content:
```js
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
2. Set the `base` of `history` mode routing, `src/app/app-routing.module.ts` file:
```diff
+ import { APP_BASE_HREF } from '@angular/common';
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
// @ts-ignore
+ providers: [{ provide: APP_BASE_HREF, useValue: window.__POWERED_BY_QIANKUN__ ? '/app-angular' : '/' }]
})
```
3. Modify the entry file, `src/main.ts` file:
```ts
import './public-path';
import { enableProdMode, NgModuleRef } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
let app: void | NgModuleRef;
async function render() {
app = await platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
}
if (!(window as any).__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap (props: Object) {
console.log(props);
}
export async function mount (props: Object) {
render();
}
export async function unmount (props: Object) {
console.log(props);
// @ts-ignore
app.destroy();
}
```
4. Modify `webpack` bundling configuration
First install the `@angular-builders/custom-webpack` plugin. **Note: `Angular 9` project can only install `9.x` version, `angular 10` project can install the latest version**.
```bash
npm i @angular-builders/custom-webpack@9.2.0 -D
```
Add `custom-webpack.config.js` to the root directory with the content:
```js
const appName = require('./package.json').name;
module.exports = {
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
output: {
library: `${appName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${appName}`,
},
};
```
Modify `angular.json`, change the values of `[packageName]> architect> build> builder` and `[packageName]> architect> serve> builder` to the plugins we installed, and add our webpack's configuration file to `[ packageName]> architect> build> options`.
```diff
- "builder": "@angular-devkit/build-angular:browser",
+ "builder": "@angular-builders/custom-webpack:browser",
"options": {
+ "customWebpackConfig": {
+ "path": "./custom-webpack.config.js"
+ }
}
```
```diff
- "builder": "@angular-devkit/build-angular:dev-server",
+ "builder": "@angular-builders/custom-webpack:dev-server",
```
5. Solve the problem of `zone.js`
Import `zone.js` in **main app**, it needs to be imported before `import qiankun`.
Delete the code of import `zone.js` in the `src/polyfills.ts` of the micro app.
```diff
- import 'zone.js/dist/zone';
```
Add the following content to the `` tag in the `src/index.html` of the micro app, which is used when the micro app is accessed independently.
```html
```
6. Fix `ng build` comand's error report, modify `tsconfig.json` file, reference[issues/431](https://github.com/umijs/qiankun/issues/431).
```diff
- "target": "es2015",
+ "target": "es5",
+ "typeRoots": [
+ "node_modules/@types"
+ ],
```
7. In order to prevent the conflict of `` when the main app or other micro apps are also `angular`, it is recommended to add a unique id to ``, such as Say the current app name.
src/index.html :
```diff
-
+
```
src/app/app.component.ts :
```diff
- selector: 'app-root',
+ selector: '#angular9 app-root',
```
Of course, you can also choose to use the `single-spa-angular` plugin, refer to[ single-spa-angular official website](https://single-spa.js.org/docs/ecosystem-angular) 和 [angular demo](https://github.com/umijs/qiankun/tree/master/examples/angular9)
### Micro app built without webpack
Some apps that are not built by `webpack`, such as `jQuery` app, `jsp` app, can be handled according to this.
Before modify, please make sure that the resources such as pictures, audio and video in your project can be loaded normally. If the addresses of these resources are all full paths (for example, `https://qiankun.umijs.org/logo.png`), there is no problem. If they are all relative paths, you need to upload these resources to the server first and reference the full path.
The only change is that we need to declare a script tag, to export the `lifecycles`
example:
1. declare entry script
```diff
Purehtml Example
Purehtml Example
+
```
2. export lifecycles in the entry
```javascript
const render = ($) => {
$('#purehtml-container').html("Hello, render with jQuery");
return Promise.resolve();
}
(global => {
global['purehtml'] = {
bootstrap: () => {
console.log('purehtml bootstrap');
return Promise.resolve();
},
mount: () => {
console.log('purehtml mount');
return render($)
},
unmount: () => {
console.log('purehtml unmount');
return Promise.resolve();
},
};
})(window);
```
refer to the [purehtml examples](https://github.com/umijs/qiankun/tree/master/examples/purehtml)
At the same time, [the subApp must support the CORS](#must-a-sub-app-asset-support-cors)
### umi-qiankun app
For the tutorial of `umi-qiankun`, please go to [umi official website](https://umijs.org/zh-CN/plugins/plugin-qiankun) and [umi-qiankun official demo](https://github.com/umijs/umi-plugin-qiankun/tree/master/examples)