npm package: @fescotech/tao
Description: 道-微前端中以iframe形式加载子应用
Author: ultra_bojack
Latest version: 0.2.0

# Tao - 道

Tao 是一个基于 iframe 实现的微前端集成方案，可以在主应用中以 iframe 的形式集成子应用，同时提供了与 qiankun 子应用共用的通信 api，与 qiankun 一起承载微前端的架构实现。

## 安装

```bash
npm i -S @fescotech/tao@latest
```

## api 说明

目前提供包括加载子应用、通信等 4 个接口

- loadMicroAppIframe: 加载 iframe 子应用
- getAppMode: 获取 app 当前运行模式
- initIframeBridge: 初始化 iframe 通信 bridge
- callMasterApi: 子应用通过该接口调用主应用暴露的 api

### `loadMicroAppIframe(app)`

手动加载/卸载 iframe 子应用

#### 参数

- app - object - 必选，微应用的基础信息

  - name - string - 必选，微应用的名称，确保在应用间唯一
  - entry - string - 必选，微应用的入口地址， 如 `//localhost:8080`
  - container - string - 必选，微应用的容器节点选择器，如 `#iframe-container`
  - props - object - 可选，初始化时传递给微应用的数据

#### 返回值

微应用的实例，实例包含以下属性/接口，可根据实际情况在合适时机调用

- 挂载应用 - mount(): Promise
- 卸载应用 - unmount(): Promise
- 应用启动结果 - bootstrapPromise: Promise
- 应用挂载结果 - mountPromise: Promise
- 应用卸载结果 - unmountPromise: Promise

#### 用法/示例

以 react 主应用中加载一个 iframe 子应用为例

```js
import { loadMicroAppIframe } from "@fescotech/tao";
import React from "react";

class App extends React.Component {
  microApp = null;

  componentDidMount() {
    this.microApp = loadMicroAppIframe({
      name: "app1",
      entry: "//localhost:8080",
      container: "#iframe-container",
      props: {
        message: "hello",
      },
    });
  }

  componentWillUnmount() {
    this.microApp.unmount();
  }

  render() {
    return <div id="iframe-container"></div>;
  }
}
```

### `getAppMode()`

获取应用当前运行模式

#### 返回值

应用当前运行模式 - string

- `__independent__`: 独立运行
- `__iframe_micro_app__`: 微前端 iframe 子应用模式
- `__qiankun_micro_app__`: 微前端-乾坤子应用模式

#### 用法/示例

```js
import { getAppMode } from "@fescotech/tao";

const appMode = getAppMode();
```

### `initIframeBridge(appName)`

初始化 iframe 通信 bridge，使用 postMessage 在 iframe 之间实现跨域通信

**仅在子应用以 iframe 方式集成时需要初始化该 iframe 通信 bridge**

#### 参数

- appName - string - 可选，子应用初始化时传入子应用名称，主应用初始化时无需传参

#### 返回值

iframe 通信 bridge 实例，实例包含 send 和 extends 两个接口，在一个 iframe 中通过 extends 注册接口，然后在其他 iframe 中就可以通过 send 调用对应接口，进行消息互通。

- 发送消息 - send(targetName，method，data，callback，type): Function

  - targetName - string - 必选，目标窗口名称
  - method - string - 必选，接口名称
  - data - object - 必选，消息数据
  - callback - function - 可选，消息回调
  - type - string - 可选，消息类型

- 注册接口 - extends(name，func): Function
  - name - string - 必选， 接口名称
  - func - function - 必选，接口函数

#### 用法/示例

比如，主应用初始化 `call_master_api` 接口，以供子应用调用

```js
IframeBridgeInstance.extends("call_master_api"，function (data) {
  console.log("调用主应用接口："，data);
  const method = data.method;
  const params = data.params;
  const result = window.__FESCOIE_TAO_MASTER_APIS__[method](params);
  console.log(
    `调用主应用__FESCOIE_TAO_MASTER_APIS__.${method},参数params:[${JSON.stringify(
      params
    )}],返回结果: `,
    result
  );
  return result;
});
```

然后在 iframe 子应用中同样初始化 iframe 通信 bridge 后，调用已在主应用注册的 `call_master_api` 方法

```js
IframeBridgeInstance.send(
  "master",
  "call_master_api",
  { method: api，params },
  (data) => {
    console.log(data);
  }
);
```

### `callMasterApi(api，params)`

调用主应用暴露的 api 从而实现子应用向主应用通信。主应用提供接口集合并暴露，按照一定的约定方式供子应用调用，屏蔽 iframe 子应用和 qiankun 子应用的通信差异。

#### 参数

- api - sting - 必选，调用 api 名称，必须已经在主应用的 `window.FESCOIE_TAO_MASTER_APIS` 中暴露出来
- params - object - 可选，调用 api 的参数

#### 返回值

接口调用返回值 - promise

#### 用法/示例

##### 1. 主应用定义接口集合并调用@fescotech/tao 中的 initMasterApis 进行初始化

```js
// 主应用 main.js
import { message } from "antd";
import { initMasterApis } from "@fescotech/tao";

// 主应用暴露给子应用的api集合
const masterApi = {
  // 获取用户信息
  getUserInfo: function (params: any) {
    let userInfo;
    if (params.type === "1") {
      userInfo = {
        type: "1",
        name: "李四",
        age: 18,
      };
    } else {
      userInfo = {
        type: "0",
        name: "张三",
        age: 18,
      };
    }
    return userInfo;
  },
  // 弹出消息
  showMessage: function (params: any) {
    message.success(params.text);
  },
};

// 初始化api集合
initMasterApis(masterApi);
```

##### 2.子应用初始化 iframe 通信通道

- 引入 tao 中的 initFrameBridge 并初始化
- name 参数为包名，需与在主应用注册的应用名称一致。
- 如果子应用确定不会以 iframe 形式接入，则可跳过此步骤

```js
// main.js中
import { initIframeBridge } from "@fescotech/tao";
const { name } = require("../package.json");
initIframeBridge(name);
```

#### 3.子应用调用主应用 api

- iframe 子应用和 qiankun 子应用调用方式相同

- 调用前需引入 tao 中的 callMasterApi，然后使用此接口调用主应用 api，返回 promise

以 Vue2 子应用为例

```js
import { callMasterApi } from "@fescotech/tao";

export default {
  data() {
    return {
      userInfo: {}，// 用户信息
    };
  },
  methods: {
    // 从主应用获取用户信息
    getUserInfoFromMaster() {
      callMasterApi("getUserInfo"，{ type: "1" })
        .then((res) => {
          if (res.code === 0) {
            this.userInfo = res.data;
          }
        })
        .catch((error) => {
          console.log(`子应用中调用主应用接口getUserInfo error： `，error);
        });
    },
    // 主应用弹出消息
    async showMessageInMaster() {
      await ("showMessage"，{ text: "消息来自子应用！" });
    },
  },
};
```