# 火山引擎 A/B 测试开放 SDK

## 介绍

火山引擎 A/B 测试开放 SDK 方便用户集成 A/B 页面，提升集成体验。提供微前端/iframe 都集成方式，省掉用户大部分的手动配置。

## 安装

```bash
npm install @datatester/op
```

## Api

### 微前端（MFE）加载方式

#### opInit（for React & Vue）

使用微前端加载器时需要提前执行 opInit，opInit 的作用是初始化微前端相关的资源。

| 字段               | 子类型    | 类型                   | 说明                                                                                                                                                                                                                                                                     | 是否必填 | 默认值                         |
| ------------------ | --------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------------------------------ |
| remote             |           | string                 | 微前端资源链接                                                                                                                                                                                                                                                           | 是       | -                              |
| base               |           | string \| string[]     | 主项目的路由前缀，用于进行主项目和微前端应用间进行路由映射，看见恶意以字符串的形式传入单个 base，也可以以数组的形式传入多个 base                                                                                                                                         | 是       | -                              |
| source             |           | string                 | 依赖的 AB 网站源地址，主要用于私有化环境的配置                                                                                                                                                                                                                           | 否       | https://console.volcengine.com |
| styleEncapsulation |           | enum                   | 样式隔离策略                                                                                                                                                                                                                                                             | 否       | styleGC                        |
|                    | styleGC   |                        | 记录载入应用所插入的样式（包括 css-in-js 样式），并在卸载时移除，载入时确保样式在插入节点的后面位置以确保高优先级                                                                                                                                                        |          |                                |
|                    | scopedCSS |                        | 劫持载入应用的样式（包括 css-in-js 样式），并对其添加该应用特有的前缀，并将组件范围外的样式映射到组件根节点的 wrapper 上，以确保该应用样式不会影响主应用，并且自身样式拥有更高优先级。                                                                                    |          |                                |
|                    | shadowDOM |                        | 将应用样式隔离在 shadowDOM 中，以确保完全的隔离                                                                                                                                                                                                                          |          |                                |
|                    | null      |                        | 关闭样式隔离                                                                                                                                                                                                                                                             |          |                                |
| memoryHistory      |           | boolean                | 开启浏览器内存记录 history                                                                                                                                                                                                                                               | 否       | false                          |
|                    | true      |                        | 开启浏览器内存记录 history，开启后子应用不会影响到主应用的路由，但网页也无法保存子应用的状态                                                                                                                                                                             |          |                                |
|                    | false     |                        | 使用主应用路由记录子应用的 history                                                                                                                                                                                                                                       |          |                                |
| mapping            |           | Record<string, string> | mapping 是一个对象，左边的 key 代表主应用的路径表达式，右边的 value 代表子应用的路径表达式。规则的匹配顺序按照 Object.keys 的顺序进行，当路由命中时后面的规则将不生效。**注意：如果配置了 mapping，则会按照 mapping 的配置进行路由映射，而不会按照 base 进行路由映射。** | 否       | -                              |
| systemjs           |           | string｜boolean         | 手动传递systemjs地址，如不传或者传true则使用默认值，传递false直接屏蔽systemjs注入。                                                                                                                                                                                                                                                           | 否       | -                              |
| environment        |           | string                 | 客户接入AB服务的环境类型                                                                                                                                                                                                                                                          | 否       | default                              |
|                    | default   |                        | 默认值为 default，不做任何处理                                                                                                                                                                                                                                                       |        |                                |
|                    | volc      |                        | 表示火山云环境，如果用户接入的 AB服务基于火山云环境，此项必填。                                                                                                                                                                                                                                                          |        |                                |  |    volc       |                  | 表示火山云环境，当前在火山云环境下必填。                                                                                                                                                                                                                                                          |        |                               |
|                    | private   |                        | 表示私有化环境                                                                                                                                                                                                                                                          |        |                                ||    private       |                  | 表示私有化环境填。                                                                                                                                                                                                                                                          |        |                               |


**示例**

```javascript
import { opInit } from '@datatester/op';

opInit({
  remote: 'https://ab.resource.path/remote.js',
  // 下述配置可以令主应用路由：/father/test/a/child/report 映射到微前端路由：/child/report
  base: '/father/test/a';
});
```

#### MfeLoader

MfeLoader 用于加载微前端应用/页面，是一个组件，其 api 如下：

| 字段          | 子字段                        | 类型                                               | 说明                                                                                       | 是否必填                               | 默认值 |
| ------------- | ----------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------- | ------ |
| type          |                               | enum                                               | 入口类型，第一次进入时默认打开的页面                                                       | 是                                     | 'list' |
|               | 'list'                        |                                                    | 实验列表                                                                                   |                                        |        |
|               | 'report'                      |                                                    | 实验报告                                                                                   |                                        |        |
|               | 'create'                      |                                                    | 实验创建                                                                                   |                                        |        |
|               | 'edit'                        |                                                    | 实验编辑                                                                                   |                                        |        |
|               | 'draft'                       |                                                    | 实验草稿                                                                                   |                                        |        |
| appId         |                               | number                                             | 应用 id                                                                                    | 是                                     | -      |
| flightId      |                               | number                                             | 实验 id                                                                                    | 当且仅当 type 为'report'或'edit'时必填 |        |
| eventCallback |                               | (result: {type: string, flightId: number }) =>void | 事件回调方法，用于在 tester 里面某些节点操作时返回 result 给宿主页面，辅助宿主打通 AB 数据 | 否                                     | -      |
|               | (type)'flight_create_success' |                                                    | 实验创建成功（开始调试）                                                                   |                                        |        |
|               | (type)'flight_edit_success'   |                                                    | 实验修改成功                                                                               |                                        |        |
|               | (type)'start_success'         |                                                    | 实验成功开始运行                                                                           |                                        |        |
|               | (type)'end_success'           |                                                    | 实验成功结束                                                                               |                                        |        |
|               | flightId                      |                                                    | 被操作的实验 id                                                                            |                                        |        |
| filters       |                               | Object                                             | 过滤器                                                                                     | 否                                     | -      |
|               | flight_ids                    | number[]                                           | 需要过滤的实验 id 列表                                                                     |                                        |        |
|               |                               |                                                    |                                                                                            |                                        |        |

**示例** _(for React)_

```typescript
import { MfeLoader } from '@datatester/op/react';
import React, { FC } from 'react';

const MfeComponent: FC<RouteComponentProps> = () => (
  <div>
    <div>这是宿主应用的header</div>
    <MfeLoader type="report" appId="12345" flightId="45678" templateId="12" />
  </div>
);

export default MfeComponent;
```

**示例** _(for Vue)_

```html
<template>
  <div>
    <div class="hello">
      <h1>这是宿主应用标题</h1>
      <h3>op sdk show case</h3>
    </div>
    <mfe-loader type="report" app-id="12345" flight-id="45678" template-id="12" />
  </div>
</template>

<script>
  import { MfeLoader } from '@datatester/op/vue';

  export default {
    name: 'MfePage',
    components: {
      MfeLoader,
    },
  };
</script>
```

#### createView

createView 用于创建一个新的微前端视图，是一个函数，返回一个 promise，其有两个方法：

- render: 用于渲染视图
- unmount: 用于卸载视图

具体使用请参照如下示例

**示例** for vanilla js(原生js)
```typescript
// 定义注册函数
import { createView } from "@datatester/op";

export async function setupAB(element: HTMLElement) {
  const view = await createView(element);
  view.render({
    type: "list",
    appId: "12345",
  });
  return view;
}

// 渲染视图
const el = document.getElementById("ab")!;
const view = await setupAB(el);

// 卸载视图
view.unmount();
```

### iframe 加载方式


#### IframeLoader

IframeLoader 用于通过iframe加载页面，是一个组件，其 api 如下：

  | 字段 | 类型 | 说明 | 是否必填 | 默认值 |
| ------------- | ----------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------- |
| type | enum | 入口类型，第一次进入时默认打开的页面，enum:list (实验列表)\|report (实验报告)\|create (实验创建)\|edit (实验编辑)\|draft (实验草稿列表) | 是 | list |
| appId | number | 应用id | 是 | - |
| flightId | number | 实验id | 当且仅当 type 为'report'或'edit'时必填 | - |
| filters | Object | 过滤器, {flight_ids: number[]},需要过滤的实验 id 列，仅在实验列表页面生效 | 否 | - |
| templateId | number | 定制模版ID | 仅在iframe嵌入需要指定模版时必填 | - |
| token | string | 请求open api获取token，用于打通与AB侧的鉴权 | 是 | - |

未列出iframe自身的属性  

**示例** _(for React)_

```typescript
import { IframeLoader } from '@datatester/op/react';
import React, { FC } from 'react';

const IframeComponent: FC<RouteComponentProps> = () => (
  <div>
    <div>这是宿主应用的header</div>
    <IframeLoader type="report" appId="12345" flightId="45678" templateId="12" token="xxxxxxxx" />
  </div>
);

export default MfeComponent;
```

**示例** _(for Vue)_

```html
<template>
  <div>
    <div class="hello">
      <h1>这是宿主应用标题</h1>
      <h3>op sdk show case</h3>
    </div>
    <iframe-loader type="report" app-id="12345" flight-id="45678" template-id="12" token="xxxxx" />
  </div>
</template>

<script>
  import { IframeLoader } from '@datatester/op/vue';

  export default {
    name: 'IframePage',
    components: {
      IframeLoader,
    },
  };
</script>
```

#### DefaultViewManager

由用户自主配置URL路径

**示例** _(for React)_

```typescript
import { DefaultViewManager } from '@datatester/op/iframe';
import React, { FC, useEffect } from 'react';

const IframeComponent: FC<RouteComponentProps> = () => {

  const iframeRef = useRef(null);

  useEffect(() => {
    const url = `https://e168-0-183tester.datarangers-onpremise.volces.com/datatester/app/1/experiment/list?abOpConfigId=1`;
    if (!iframeRef.current) {
      iframeRef.current = new DefaultViewManager(document.getElementById('abIframeParent') as HTMLElement, {
        iframeSrc: url,
        iframeAttrs: {
          name: `x-rgw-dor=${document.cookie?.match('(^|;)\\s*x-rgw-dor\\s*=\\s*([^;]+)')?.pop() || ''}`,
          height: '800px',
          width: '100%',
          style: 'border:0px',
        },
      });
    } else {
      const iframe = document.querySelector('#abIframeParent > iframe');
      if (iframe) {
        iframe.src = url;
      }
    }
  }, [location.pathname]);

  return <div id="abIframeParent" />;
};

export default MfeComponent;
```