492 lines
14 KiB
Markdown
492 lines
14 KiB
Markdown
---
|
||
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).
|
||
|
||
<Alert>
|
||
Note: `publicPath` at runtime and `publicPath` at build time are different, and the two cannot be equivalently substituted.
|
||
</Alert>
|
||
|
||
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
|
||
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>
|
||
```
|
||
|
||
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(<App />, 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__;
|
||
}
|
||
```
|
||
|
||
2. 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<AppModule>;
|
||
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 `<head>` tag in the `src/index.html` of the micro app, which is used when the micro app is accessed independently.
|
||
|
||
```html
|
||
<!-- Other CDN/local packages can also be used -->
|
||
<script src="https://unpkg.com/zone.js" ignore></script>
|
||
```
|
||
|
||
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 `<app-root></app-root>` when the main app or other micro apps are also `angular`, it is recommended to add a unique id to `<app-root>`, such as Say the current app name.
|
||
|
||
src/index.html :
|
||
|
||
```diff
|
||
- <app-root></app-root>
|
||
+ <app-root id="angular9"></app-root>
|
||
```
|
||
|
||
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)
|
||
|
||
(**supplement**)The angular7 has the same steps as angular9 except for step 4. The steps for angular7 to modify the `webpack` configuration are as follows:
|
||
|
||
In addition to installing the 7.x version of `angular-builders/custom-webpack`, you also need to install `angular-builders/dev-server`.
|
||
|
||
```bash
|
||
npm i @angular-builders/custom-webpack@7 -D
|
||
npm i @angular-builders/dev-server -D
|
||
```
|
||
|
||
Add `custom-webpack.config.js` to the root directory, same as above.
|
||
|
||
Modify `angular.json`, `[packageName] > architect > build > builder` is the same as Angular9, and `[packageName] > architect > serve > builder` is different from Angular9.
|
||
|
||
```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/dev-server:generic",
|
||
```
|
||
|
||
### 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
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Purehtml Example</title>
|
||
</head>
|
||
<body>
|
||
<div>
|
||
Purehtml Example
|
||
</div>
|
||
</body>
|
||
|
||
+ <script src="//yourhost/entry.js" entry></script>
|
||
</html>
|
||
```
|
||
|
||
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](/faq#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)
|