📝 add cookbook and tutorial (#1167)
Co-authored-by: gongshun <gongshun@gridsum.com> Co-authored-by: Kuitos <kuitos.lau@gmail.com>
This commit is contained in:
parent
3223092465
commit
006877c2d0
16
.umirc.ts
16
.umirc.ts
|
|
@ -17,14 +17,26 @@ export default defineConfig({
|
|||
navs: {
|
||||
en: [
|
||||
null,
|
||||
{
|
||||
title: 'Version Notice',
|
||||
children: [
|
||||
{ title: 'Changelog', path: 'https://github.com/umijs/qiankun/releases' },
|
||||
{ title: '1.x', path: 'https://v1.qiankun.umijs.org/' },
|
||||
{ title: 'Upgrade Guide', path: '/cookbook#upgrade-from-1x-version-to-2x-version' },
|
||||
{ title: '1.x Version', path: 'https://v1.qiankun.umijs.org/' },
|
||||
],
|
||||
},
|
||||
{ title: 'GitHub', path: 'https://github.com/umijs/qiankun' },
|
||||
],
|
||||
zh: [
|
||||
null,
|
||||
{
|
||||
title: '版本公告',
|
||||
children: [
|
||||
{ title: '发布日志', path: 'https://github.com/umijs/qiankun/releases' },
|
||||
{ title: '1.x', path: 'https://v1.qiankun.umijs.org/zh/' },
|
||||
{ title: '升级指南', path: '/zh/cookbook#从-1x-版本升级到-2x-版本' },
|
||||
{ title: '1.x 版本', path: 'https://v1.qiankun.umijs.org/zh/' },
|
||||
],
|
||||
},
|
||||
{ title: 'GitHub', path: 'https://github.com/umijs/qiankun' },
|
||||
],
|
||||
},
|
||||
|
|
|
|||
324
docs/cookbook/README.md
Normal file
324
docs/cookbook/README.md
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
---
|
||||
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
|
||||
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/child/react-history/'}>
|
||||
```
|
||||
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`.
|
||||
322
docs/cookbook/README.zh.md
Normal file
322
docs/cookbook/README.zh.md
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
---
|
||||
nav:
|
||||
title: 入门教程
|
||||
toc: menu
|
||||
---
|
||||
|
||||
# 入门教程
|
||||
|
||||
## 微应用的路由模式如何选择
|
||||
|
||||
`react-router`,`angular-router`,`vue-router` 这三种路由,都支持 `hash` 和 `history` 模式,微应用使用不同的模式在 `qiankun` 中略有差别。
|
||||
|
||||
### `activeRule` 使用 `location.pathname` 区分微应用
|
||||
|
||||
主应用使用 `location.pathname` 来区分微应用时,微应用可以是 `hash` 和 `history` 模式。
|
||||
|
||||
注册微应用时 `activeRule` 这样写即可:
|
||||
|
||||
```js
|
||||
registerMicroApps([
|
||||
{
|
||||
name: 'app',
|
||||
entry: 'http://localhost:8080',
|
||||
container: '#container',
|
||||
activeRule: '/app',
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
1. 当微应用是 `history` 模式时,设置路由 `base` 即可
|
||||
|
||||
2. 当微应用是 `hash` 模式时,三种路由的表现不一致
|
||||
|
||||
| 路由 | 主应用跳转/app/#/about | 特殊配置 |
|
||||
| ---------------| ----------------------| --------------------|
|
||||
| vue-router | 响应 about 路由 | 无 |
|
||||
| react-router | 不响应 about 路由 | 无 |
|
||||
| angular-router | 响应 about 路由 | 需要设置 --base-href |
|
||||
|
||||
`angular` 应用在 `package.json` 里面设置 `--base-href`:
|
||||
```diff
|
||||
- "start": "ng serve",
|
||||
+ "start": "ng serve --base-href /angular9",
|
||||
- "build": "ng build",
|
||||
+ "build": "ng build --base-href /angular9",
|
||||
```
|
||||
|
||||
打包部署后,`angular` 微应用可以被主应用访问。但是独立访问时,懒加载的路由会报错,路径不正确。这里有两个解决办法:
|
||||
|
||||
- 方法1:修改 `public-path.js` 为:
|
||||
|
||||
```js
|
||||
__webpack_public_path__ = window.__POWERED_BY_QIANKUN__ ? window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ : `http://${ip}:${port}/`; // 填写你的实际部署地址
|
||||
```
|
||||
- 方法2:修改打包命令,并且将微应用部署在 `angular9` 目录:
|
||||
|
||||
```diff
|
||||
- "build": "ng build",
|
||||
+ "build": "ng build --base-href /angular9 --deploy-url /angular9/",
|
||||
```
|
||||
|
||||
### `activeRule` 使用 `location.hash` 区分微应用
|
||||
|
||||
当微应用都是 `hash` 模式时可以使用 `hash` 区分微应用,主应用的路由模式不限。
|
||||
|
||||
注册微应用时 `activeRule` 需要这样写:
|
||||
|
||||
```js
|
||||
const getActiveRule = hash => location => location.hash.startsWith(hash);
|
||||
registerMicroApps([
|
||||
{
|
||||
name: 'app-hash',
|
||||
entry: 'http://localhost:8080',
|
||||
container: '#container',
|
||||
activeRule: getActiveRule('#/app-hash'),
|
||||
// 这里也可以直接写 activeRule: '#/app-hash',但是如果主应用是 history 模式或者主应用部署在非根目录,这样写不会生效。
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
`react-router` 和 `angular-router` 微应用需要设置 `activeRule` 的值为路由的 `base` ,写法同 `history` 模式。
|
||||
|
||||
`vue-router` 的 `hash` 模式下不支持设置路由的 `base`,需要额外新建一个空的路由页面,将其他所有路由都作为它的 `children`:
|
||||
|
||||
```js
|
||||
const routes = [
|
||||
{
|
||||
path: '/app-vue-hash',
|
||||
name: 'Home',
|
||||
component: Home,
|
||||
children: [
|
||||
// 其他的路由都写到这里
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 同时存在多个微应用时
|
||||
|
||||
如果一个页面同时展示多个微应用,需要使用 `loadMicroApp` 来加载。
|
||||
|
||||
如果这些微应用都有路由跳转的需求,要保证这些路由能互不干扰,需要使用 `momery` 路由。`vue-router` 使用 `abstract` 模式,`react-router` 使用 `memory history` 模式,`angular-router` 不支持。
|
||||
|
||||
## 如何部署
|
||||
|
||||
**建议**:主应用和微应用都是独立开发和部署,即它们都属于不同的仓库和服务。
|
||||
|
||||
### 场景1:主应用和微应用部署到同一个服务器(同一个IP和端口)
|
||||
|
||||
如果服务器数量有限,或不能跨域等原因需要把主应用和微应用部署到一起。
|
||||
|
||||
通常的做法是主应用部署在一级目录,微应用部署在二/三级目录。
|
||||
|
||||
微应用想部署在非根目录,在微应用打包之前需要做两件事:
|
||||
|
||||
1. 必须配置 `webpack` 构建时的 `publicPath` 为**目录名称**,更多信息请看 [webpack 官方说明](https://www.webpackjs.com/configuration/output/#output-publicpath) 和 [vue-cli3 的官方说明](https://cli.vuejs.org/zh/config/#publicpath)
|
||||
|
||||
2. `history` 路由的微应用需要设置 `base` ,值为**目录名称**,用于独立访问时使用。
|
||||
|
||||
部署之后注意三点:
|
||||
|
||||
1. `activeRule` 不能和**微应用的真实访问路径一样**,否则在主应用页面刷新会直接变成微应用页面。
|
||||
2. 微应用的真实访问路径就是微应用的 `entry`,`entry` 可以为相对路径。
|
||||
3. 微应用的 `entry` 路径最后面的 `/` 不可省略,否则 `publicPath` 会设置错误,例如子项的访问路径是 `http://localhost:8080/app1`,那么 `entry` 就是 `http://localhost:8080/app1/`。
|
||||
|
||||
具体的部署有以下两种方式,选择其一即可。
|
||||
|
||||
#### 方案1:微应用都放在在一个特殊名称(**不会和微应用重名**)的文件夹下(**建议使用**)
|
||||
|
||||
假设我们有一个主应用和 6 个微应用(分别为 `vue-hash`、`vue-history`、`react-hash`、`react-history`、`angular-hash`、`angular-history` ),打包后如下放置:
|
||||
```
|
||||
└── html/ # 根文件夹
|
||||
|
|
||||
├── child/ # 存放所有微应用的文件夹
|
||||
| ├── vue-hash/ # 存放微应用 vue-hash 的文件夹
|
||||
| ├── vue-history/ # 存放微应用 vue-history 的文件夹
|
||||
| ├── react-hash/ # 存放微应用 react-hash 的文件夹
|
||||
| ├── react-history/ # 存放微应用 react-history 的文件夹
|
||||
| ├── angular-hash/ # 存放微应用 angular-hash 的文件夹
|
||||
| ├── angular-history/ # 存放微应用 angular-history 的文件夹
|
||||
├── index.html # 主应用的index.html
|
||||
├── css/ # 主应用的css文件夹
|
||||
├── js/ # 主应用的js文件夹
|
||||
```
|
||||
|
||||
此时需要设置微应用构建时的 `publicPath` 和 `history` 模式的路由 `base`,然后才能打包放到对应的目录里。
|
||||
|
||||
| 项目 | 路由 base | publicPath | 真实访问路径 |
|
||||
| --------------- | ------------------------| ------------------------| ---------------------------------------------|
|
||||
| vue-hash | 无 | /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 | 无 | /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 | 无 | /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 微应用
|
||||
|
||||
路由设置:
|
||||
```js
|
||||
base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/child/vue-history/',
|
||||
```
|
||||
webpack 打包 publicPath 配置(`vue.config.js`):
|
||||
```js
|
||||
module.exports = {
|
||||
publicPath: '/child/vue-history/'
|
||||
}
|
||||
```
|
||||
|
||||
- react-history 微应用
|
||||
|
||||
路由设置:
|
||||
```html
|
||||
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/child/react-history/'}>
|
||||
```
|
||||
webpack 打包 publicPath 配置:
|
||||
```js
|
||||
module.exports = {
|
||||
output: {
|
||||
publicPath: '/child/react-history/',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- angular-history 微应用
|
||||
|
||||
路由设置:
|
||||
```js
|
||||
providers: [{
|
||||
provide: APP_BASE_HREF,
|
||||
useValue: window.__POWERED_BY_QIANKUN__ ? '/app-angular/' : '/child/angular-history/'
|
||||
}]
|
||||
```
|
||||
webpack 打包的 `publicPath` 通过 `deploy-url` 来修改,修改 `package.json`:
|
||||
```diff
|
||||
- "build": "ng build",
|
||||
+ "build": "ng build --deploy-url /child/angular-history/",
|
||||
```
|
||||
|
||||
那么此时的注册函数是这样的(需要保证 `activeRule` 和 `entry` 不同):
|
||||
|
||||
```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 和 react 同上
|
||||
],
|
||||
```
|
||||
|
||||
至此主应用已经和微应用都能跑起来了,但是主应用和 `vue-history`、`react-history`、`angular-history` 微应用是 `history` 路由,需要解决刷新 404 的问题,`nginx` 还需要配置一下:
|
||||
|
||||
```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;
|
||||
}
|
||||
# angular 和 react 的history 配置同上
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案2:微应用直接放在二级目录,但是设置特殊的 `activeRule`
|
||||
|
||||
```
|
||||
└── html/ # 根文件夹
|
||||
|
|
||||
├── vue-hash/ # 存放微应用 vue-hash 的文件夹
|
||||
├── vue-history/ # 存放微应用 vue-history 的文件夹
|
||||
├── react-hash/ # 存放微应用 react-hash 的文件夹
|
||||
├── react-history/ # 存放微应用 react-history 的文件夹
|
||||
├── angular-hash/ # 存放微应用 angular-hash 的文件夹
|
||||
├── angular-history/ # 存放微应用 angular-history 的文件夹
|
||||
├── index.html # 主应用的index.html
|
||||
├── css/ # 主应用的css文件夹
|
||||
├── js/ # 主应用的js文件夹
|
||||
```
|
||||
|
||||
基本操作和上面是一样的,只要保证 `activeRule` 和微应用的存放路径名不一样即可。
|
||||
|
||||
### 场景2:主应用和微应用部署在不同的服务器,使用 Nginx 代理访问
|
||||
|
||||
一般这么做是因为**不允许主应用跨域访问微应用**,做法就是将主应用服务器上一个特殊路径的请求全部转发到微应用的服务器上,即通过代理实现“微应用部署在主应用服务器上”的效果。
|
||||
|
||||
例如,主应用在 A 服务器,微应用在 B 服务器,使用路径 `/app1` 来区分微应用,即 A 服务器上所有 `/app1` 开头的请求都转发到 B 服务器上。
|
||||
|
||||
此时主应用的 `Nginx` 代理配置为:
|
||||
|
||||
```
|
||||
/app1/ {
|
||||
proxy_pass http://www.b.com/app1/;
|
||||
proxy_set_header Host $host:$server_port;
|
||||
}
|
||||
```
|
||||
|
||||
主应用注册微应用时,`entry` 可以为相对路径,`activeRule` 不可以和 `entry` 一样(否则主应用页面刷新就变成微应用):
|
||||
|
||||
```js
|
||||
registerMicroApps([
|
||||
{
|
||||
name: 'app1',
|
||||
entry: '/app1/', // http://localhost:8080/app1/
|
||||
container: '#container',
|
||||
activeRule: '/child-app1',
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
对于 `webpack` 构建的微应用,微应用的 `webpack` 打包的 `publicPath` 需要配置成 `/app1/`,否则微应用的 `index.html` 能正确请求,但是微应用 `index.html` 里面的 `js/css` 路径不会带上 `/app1/`。
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
output: {
|
||||
publicPath: `/app1/`,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
微应用打包的 `publicPath` 加上 `/app1/` 之后,必须部署在 `/app1` 目录,否则无法独立访问。
|
||||
|
||||
另外,如果不想微应用通过代理路径被独立访问,可以根据请求的一些信息判断下,主应用中请求微应用是用 `fetch` 请求的,可以带参数和 `cookie`。例如通过请求头参数判断:
|
||||
|
||||
```js
|
||||
if ($http_custom_referer != "main") {
|
||||
rewrite /index /404.html;
|
||||
}
|
||||
```
|
||||
|
||||
## 从 1.x 版本升级到 2.x 版本
|
||||
|
||||
微应用无需改动,主应用需要做一些调整。
|
||||
|
||||
`registerMicroApps` 函数基本修改如下:
|
||||
|
||||
1. 去掉 `render` 参数,只需要提供容器 `container` 即可。
|
||||
2. 增加 `loader` 参数,用于展示 `loading` 状态,原本 `loading` 状态是提供给 `render` 参数的。
|
||||
3. `activeRule` 参数可以简写为 `/app`,兼容之前的函数写法。
|
||||
4. `RegisterMicroAppsOpts` 参数去掉了,放在了 `start` 函数的参数里面。
|
||||
|
||||
`start` 函数基本修改如下:
|
||||
|
||||
1. `jsSandbox` 配置去掉,改为 `sandbox` ,可选值也修改了。
|
||||
2. 新增了 `getPublicPath` 和 `getTemplate` ,用于替代`RegisterMicroAppsOpts`。
|
||||
|
|
@ -171,21 +171,6 @@ 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; window.Vue = undefined`
|
||||
|
||||
## Tips on Using Vue Router
|
||||
|
||||
The qiankun main app activates the corresponding micro app according to the `activeRule` configuration.
|
||||
|
||||
### a. The main app is using Vue Router's hash mode
|
||||
|
||||
When the main app is in hash mode, generally, micro app is also in hash mode. In this case, the base hash path of the main app is assigned to the corresponding micro app (e.g. `#base`). At this time, if the micro app needs to make a secondary path jump in hash mode (such as `#/base1/child1`) when there is a base path, you just need to add a prefix for each route yourself.
|
||||
The base parameter in VueRouter's hash mode [does not support adding a hash path base](https://github.com/vuejs/vue-router/blob/dev/src/index.js#L55-L69).
|
||||
|
||||
### b. The main app is using Vue Router's history mode
|
||||
|
||||
When the main app is in history mode and the micro app is also in history mode, it works perfectly. And if the micro app needs to add a base path, just [set the base property](https://router.vuejs.org/api/#base) of the sub item.
|
||||
|
||||
When the main app is in history mode and the micro app is in hash mode, it works perfectly.
|
||||
|
||||
## Why dynamic imported assets missing?
|
||||
|
||||
Two way to solve that:
|
||||
|
|
@ -545,64 +530,6 @@ import 'core-js/web/url';
|
|||
|
||||
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.
|
||||
|
||||
## Does qiankun support the subApp without bundler?
|
||||
|
||||
> Yes
|
||||
|
||||
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](#must-a-sub-app-asset-support-cors)
|
||||
|
||||
## 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.
|
||||
|
|
|
|||
|
|
@ -172,20 +172,6 @@ if (inBrowser && window.Vue) {
|
|||
1. 在主应用中不使用 CDN 等 external 的方式来加载 `Vue` 框架,使用前端打包软件来加载模块
|
||||
2. 在主应用中,将 `window.Vue` 变量改个名称,例如 `window.Vue2 = window.Vue; window.Vue = undefined`
|
||||
|
||||
## Vue 框架下使用 Vue Router 的注意点
|
||||
|
||||
qiankun 主应用根据 `activeRule` 配置激活对应微应用。
|
||||
|
||||
### a. 主应用是 hash 模式
|
||||
|
||||
当主应用是 hash 模式时,一般微应用也是 hash 模式。主应用的一级 hash 路径会分配给对应的微应用(比如 `#/base1` ),此时微应用如果需要在 base 路径的基础上进行 hash 模式下的二级路径跳转(比如 `#/base1/child1` ),这个场景在当前 VueRouter 的实现方式下需要自己手动实现,给所有路由都添加一个前缀即可。VueRouter 的 hash 模式下的 base 参数[不支持添加 hash 路径 base](https://github.com/vuejs/vue-router/blob/dev/src/index.js#L55-L69)。
|
||||
|
||||
### b. 主应用是 history 模式
|
||||
|
||||
当主应用是 history 模式且微应用也是 history 模式时,表现完美。如果微应用需要添加 base 路径,设置微应用的 [base](https://router.vuejs.org/zh/api/#base) 属性即可。
|
||||
|
||||
当主应用是 history 模式,微应用是 hash 模式,表现完美。
|
||||
|
||||
## 为什么微应用加载的资源会 404?
|
||||
|
||||
原因是 webpack 加载资源时未使用正确的 `publicPath`。
|
||||
|
|
@ -615,64 +601,6 @@ import 'core-js/web/url';
|
|||
|
||||
qiankun 依赖的 import-html-entry 通过 `window.fetch` 来获取微应用的资源,部分[不支持 fetch 的浏览器](https://caniuse.com/#search=fetch)需要在入口处打上相应的 [polyfill](https://github.com/github/fetch)
|
||||
|
||||
## 非 webpack 构建的微应用支持接入 qiankun 么?
|
||||
|
||||
> 支持
|
||||
|
||||
需要额外声明一个 `script`,用于 `export` 相对应的 `lifecycles`
|
||||
|
||||
例如:
|
||||
|
||||
1. 声明 entry 入口
|
||||
|
||||
```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. 在 entry js 里声明 lifecycles
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
你也可以直接参照 examples 中 purehtml 部分的[代码](https://github.com/umijs/qiankun/tree/master/examples/purehtml)
|
||||
|
||||
同时,你也需要开启相关资源的 CORS,具体请参照[此处](#微应用静态资源一定要支持跨域吗?)
|
||||
|
||||
## 微应用 JSONP 跨域错误怎么处理?
|
||||
|
||||
qiankun 会将微应用的动态 script 加载(例如 JSONP)转化为 fetch 请求,因此需要相应的后端服务支持跨域,否则会导致错误。
|
||||
|
|
|
|||
457
docs/guide/tutorial.md
Normal file
457
docs/guide/tutorial.md
Normal file
|
|
@ -0,0 +1,457 @@
|
|||
---
|
||||
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. The `history` route needs to set the route `base`, and the value is the same as its `activeRule`. At this time, the `hash` mode does not need to set the route `base` (when the `hash` is used to distinguish micro apps, the `hash` route only need to set the route `base`).
|
||||
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
|
||||
<!-- Not required for hash mode -->
|
||||
<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__;
|
||||
}
|
||||
```
|
||||
|
||||
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({
|
||||
// hash Mode does not need to set base
|
||||
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');
|
||||
}
|
||||
|
||||
// standalone
|
||||
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)
|
||||
|
||||
### 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](#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)
|
||||
483
docs/guide/tutorial.zh.md
Normal file
483
docs/guide/tutorial.zh.md
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
---
|
||||
toc: menu
|
||||
---
|
||||
|
||||
# 项目实践
|
||||
|
||||
本教程适合刚接触 `qiankun` 的新人,介绍了如何从 0 构建一个 `qiankun` 项目。
|
||||
|
||||
## 主应用
|
||||
|
||||
主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 `start` 即可。
|
||||
|
||||
先安装 `qiankun` :
|
||||
|
||||
```shell
|
||||
$ yarn add qiankun # 或者 npm i qiankun -S
|
||||
```
|
||||
|
||||
注册微应用并启动:
|
||||
|
||||
```js
|
||||
import { registerMicroApps, start } from 'qiankun';
|
||||
|
||||
registerMicroApps([
|
||||
{
|
||||
name: 'reactApp',
|
||||
entry: '//localhost:3000',
|
||||
container: '#container',
|
||||
activeRule: '/app-react',
|
||||
},
|
||||
{
|
||||
name: 'vueApp',
|
||||
entry: '//localhost:8080',
|
||||
container: '#container',
|
||||
activeRule: '/app-vue',
|
||||
},
|
||||
{
|
||||
name: 'angularApp',
|
||||
entry: '//localhost:4200',
|
||||
container: '#container',
|
||||
activeRule: '/app-angular',
|
||||
},
|
||||
]);
|
||||
// 启动 qiankun
|
||||
start();
|
||||
```
|
||||
|
||||
## 微应用
|
||||
|
||||
微应用分为有 `webpack` 构建和无 `webpack` 构建项目,有 `webpack` 的微应用(主要是指Vue、React、Angular)需要做的事情有:
|
||||
|
||||
1. 新增 `public-path.js` 文件,用于修改运行时的 `publicPath`。[什么是运行时的 publicPath ?](https://webpack.docschina.org/guides/public-path/#on-the-fly)。
|
||||
|
||||
<Alert>
|
||||
注意:运行时的 publicPath 和构建时的 publicPath 是不同的,两者不能等价替代。
|
||||
</Alert>
|
||||
|
||||
2. `history` 路由需要设置路由 `base`,值和它的 `activeRule` 是一样的,此时 `hash` 模式无需设置路由 `base`(当使用 `hash` 来区分微应用时,`hash` 路由才需要设置路由 `base`)。
|
||||
3. 在入口文件最顶部引入 `public-path.js`,修改并导出三个生命周期函数。
|
||||
4. 修改 `webpack` 打包,允许开发环境跨域和 `umd` 打包。
|
||||
|
||||
主要的修改就是以上四个,可能会根据项目的不同情况而改变。例如,你的项目是 `index.html` 和其他的所有文件分开部署的,说明你们已经将构建时的 `publicPath` 设置为了完整路径,则不用修改运行时的 `publicPath` (第一步操作可省)。
|
||||
|
||||
无 `webpack` 构建的微应用直接将 `lifecycles` 挂载到 `window` 上即可。
|
||||
|
||||
### React 微应用
|
||||
|
||||
以 `create react app` 生成的 `react 16` 项目为例,搭配 `react-router-dom` 5.x。
|
||||
|
||||
1. 在 `src` 目录新增 `public-path.js`:
|
||||
|
||||
```js
|
||||
if (window.__POWERED_BY_QIANKUN__) {
|
||||
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
|
||||
}
|
||||
```
|
||||
2. 设置 `history` 模式路由的 `base`:
|
||||
|
||||
```html
|
||||
<!-- hash 模式不需要 -->
|
||||
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>
|
||||
```
|
||||
|
||||
3. 入口文件 `index.js` 修改,为了避免根 id `#root` 与其他的 DOM 冲突,需要限制查找范围。
|
||||
|
||||
```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'));
|
||||
}
|
||||
|
||||
function storeTest(props) {
|
||||
props.onGlobalStateChange((value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true);
|
||||
props.setGlobalState({
|
||||
ignore: props.name,
|
||||
user: {
|
||||
name: props.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
storeTest(props);
|
||||
render(props);
|
||||
}
|
||||
|
||||
export async function unmount(props) {
|
||||
const { container } = props;
|
||||
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
|
||||
}
|
||||
```
|
||||
|
||||
4. 修改 `webpack` 配置
|
||||
|
||||
安装插件 `@rescripts/cli`,当然也可以选择其他的插件,例如 `react-app-rewired`。
|
||||
```dash
|
||||
npm i -D @rescripts/cli
|
||||
```
|
||||
|
||||
根目录新增 `.rescriptsrc.js`:
|
||||
```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;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
修改 `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 微应用
|
||||
|
||||
以 `vue-cli 3+` 生成的 `vue 2.x` 项目为例,`vue 3` 版本等稳定后再补充。
|
||||
|
||||
1. 在 `src` 目录新增 `public-path.js`:
|
||||
|
||||
```js
|
||||
if (window.__POWERED_BY_QIANKUN__) {
|
||||
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
|
||||
}
|
||||
```
|
||||
|
||||
3. 入口文件 `main.js` 修改,为了避免根 id `#app` 与其他的 DOM 冲突,需要限制查找范围。
|
||||
|
||||
```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({
|
||||
// hash 模式不需要设置 base
|
||||
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');
|
||||
}
|
||||
|
||||
// 独立运行时
|
||||
if (!window.__POWERED_BY_QIANKUN__) {
|
||||
render();
|
||||
}
|
||||
|
||||
function storeTest(props) {
|
||||
props.onGlobalStateChange &&
|
||||
props.onGlobalStateChange(
|
||||
(value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev),
|
||||
true,
|
||||
);
|
||||
props.setGlobalState &&
|
||||
props.setGlobalState({
|
||||
ignore: props.name,
|
||||
user: {
|
||||
name: props.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function bootstrap() {
|
||||
console.log('[vue] vue app bootstraped');
|
||||
}
|
||||
export async function mount(props) {
|
||||
console.log('[vue] props from main framework', props);
|
||||
storeTest(props); // 测试双向传递数据
|
||||
render(props);
|
||||
}
|
||||
export async function unmount() {
|
||||
instance.$destroy();
|
||||
instance.$el.innerHTML = '';
|
||||
instance = null;
|
||||
router = null;
|
||||
}
|
||||
```
|
||||
|
||||
3. 打包配置修改(`vue.config.js`):
|
||||
|
||||
```js
|
||||
const { name } = require('./package');
|
||||
module.exports = {
|
||||
devServer: {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
},
|
||||
configureWebpack: {
|
||||
output: {
|
||||
library: `${name}-[name]`,
|
||||
libraryTarget: 'umd',// 把微应用打包成 umd 库格式
|
||||
jsonpFunction: `webpackJsonp_${name}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Angular 微应用
|
||||
|
||||
以 `Angular-cli 9` 生成的 `angular 9` 项目为例,其他版本的 `angular` 后续会逐渐补充。
|
||||
|
||||
1. 在 `src` 目录新增 `public-path.js` 文件,内容为:
|
||||
|
||||
```js
|
||||
if (window.__POWERED_BY_QIANKUN__) {
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
|
||||
}
|
||||
```
|
||||
|
||||
2. 设置 `history` 模式路由的 `base`,`src/app/app-routing.module.ts` 文件:
|
||||
|
||||
```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. 修改入口文件,`src/main.ts` 文件。
|
||||
|
||||
```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. 修改 `webpack` 打包配置
|
||||
|
||||
先安装 `@angular-builders/custom-webpack` 插件,**注意:`angular 9` 项目只能安装 `9.x` 版本,`angular 10` 项目可以安装最新版**。
|
||||
|
||||
```bash
|
||||
npm i @angular-builders/custom-webpack@9.2.0 -D
|
||||
```
|
||||
|
||||
在根目录增加 `custom-webpack.config.js` ,内容为:
|
||||
|
||||
```js
|
||||
const appName = require('./package.json').name;
|
||||
module.exports = {
|
||||
devServer: {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
},
|
||||
output: {
|
||||
library: `${appName}-[name]`,
|
||||
libraryTarget: 'umd',
|
||||
jsonpFunction: `webpackJsonp_${appName}`,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
修改 `angular.json`,将 `[packageName] > architect > build > builder` 和 `[packageName] > architect > serve > builder` 的值改为我们安装的插件,将我们的打包配置文件加入到 `[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. 解决 `zone.js` 的问题
|
||||
|
||||
在**父应用**引入 `zone.js`,需要在 `import qiankun` 之前引入。
|
||||
|
||||
将微应用的 `src/polyfills.ts` 里面的引入 `zone.js` 代码删掉。
|
||||
|
||||
```diff
|
||||
- import 'zone.js/dist/zone';
|
||||
```
|
||||
|
||||
在微应用的 `src/index.html` 里面的 `<head>` 标签加上下面内容,微应用独立访问时使用。
|
||||
|
||||
```html
|
||||
<!-- 也可以使用其他的CDN/本地的包 -->
|
||||
<script src="https://unpkg.com/zone.js" ignore></script>
|
||||
```
|
||||
|
||||
6. 修正 `ng build` 打包报错问题,修改 `tsconfig.json` 文件,参考[issues/431](https://github.com/umijs/qiankun/issues/431)
|
||||
|
||||
```diff
|
||||
- "target": "es2015",
|
||||
+ "target": "es5",
|
||||
+ "typeRoots": [
|
||||
+ "node_modules/@types"
|
||||
+ ],
|
||||
```
|
||||
|
||||
7. 为了防止主应用或其他微应用也为 `angular` 时,`<app-root></app-root>` 会冲突的问题,建议给`<app-root>` 加上一个唯一的 id,比如说当前应用名称。
|
||||
|
||||
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',
|
||||
```
|
||||
|
||||
当然,也可以选择使用 `single-spa-angular` 插件,参考[ single-spa-angular 的官网](https://single-spa.js.org/docs/ecosystem-angular) 和 [angular demo](https://github.com/umijs/qiankun/tree/master/examples/angular9)
|
||||
|
||||
### 非 webpack 构建的微应用
|
||||
|
||||
一些非 `webpack` 构建的项目,例如 `jQuery` 项目、`jsp` 项目,都可以按照这个处理。
|
||||
|
||||
接入之前请确保你的项目里的图片、音视频等资源能正常加载,如果这些资源的地址都是完整路径(例如 `https://qiankun.umijs.org/logo.png`),则没问题。如果都是相对路径,需要先将这些资源上传到服务器,使用完整路径。
|
||||
|
||||
接入非常简单,只需要额外声明一个 `script`,用于 `export` 相对应的 `lifecycles`。例如:
|
||||
|
||||
1. 声明 entry 入口
|
||||
|
||||
```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. 在 entry js 里声明 lifecycles
|
||||
|
||||
```js
|
||||
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);
|
||||
```
|
||||
|
||||
你也可以直接参照 examples 中 purehtml 部分的[代码](https://github.com/umijs/qiankun/tree/master/examples/purehtml)
|
||||
|
||||
同时,你也需要开启相关资源的 CORS,具体请参照[此处](#微应用静态资源一定要支持跨域吗?)
|
||||
|
||||
### umi-qiankun 项目
|
||||
|
||||
`umi-qiankun` 的教程请移步 [umi 官网](https://umijs.org/zh-CN/plugins/plugin-qiankun) 和 [umi-qiankun 的官方 demo](https://github.com/umijs/umi-plugin-qiankun/tree/master/examples)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user