W 011 postmessage包

134 阅读3分钟

1.背景

在公司低代码平台项目中, 有人封装了一个高度定制化的包, 但存在如下缺点:

    1. 高度定制化, 不支持功能和业务扩展
    1. 由于包封装原因, 业务代码在使用过程中紊乱分散, 不利于后续委会
    1. 当前业务中新增通信链路必须侵入npm包内, 增加调试成本
    1. 包内代码存在坏味道

common-postmessage具备业务通用的postMessage实现, 其内置单例模式和各种边界情况的兜底处理, 父子页面分别在初始化时输入策略模式, 则自动监听整条通信链路并根据通信数据触发对应的事件行为。

2.对外API说明

2-1.父页面API

/** 父页面 接口定义 */
export interface ParentWindowInterface<ParentPostAction extends ActionValType> {
  /** 发送数据给子页面 */
  postChildMesage(iframeName: string, data: PostMessageData<ParentPostAction>): void;
  /** 获取当前页面下所有 Iframe 的初始化情况 */
  getIframeInitMap(): any;
  /** 记录当前页面下所有 Iframe 的初始化完成情况 */
  updateIframeInitMap(iframeName: string): void;
  /** 获取自定义集合 */
  getCustomizedMap(): any;
  /** 增量更新自定义集合 */
  updateCustomizedMap(data: any): void;
}

/** 父页面 Props定义 */
export interface ParentWindowProps<ChildPostAction extends ActionValType> {
  /** 父页面要监听message的策略模式对象 */
  pageActionMap: PageActionMap<ChildPostAction>;
  /** 父页面自定义配置, 如父页面postmessage事件等 */
  customizedMap: any
}

2-2.子页面API

/** 子页面 接口定义 */
export interface ChildWindowInterface<ChildPostAction extends ActionValType> {
  /** 发送数据给父页面 */
  postFatherMesage(data: PostMessageData<ChildPostAction>): void;
  /** 获取自定义集合 */
  getCustomizedMap(): any;
  /** 增量更新自定义集合 */
  updateCustomizedMap(data: any): void;
}

/** 子页面 Props定义 */
export interface ChildWindowProps<ParentPostAction extends ActionValType> {
  /** 子页面要监听message的策略模式对象 */
  pageActionMap: PageActionMap<ParentPostAction>;
  /** 子页面自定义配置, 如子页面postmessage事件等 */
  customizedMap: any
}

3. 使用范例

父子页面使用的通用配置, 如下:

建议 :枚举均采用独一无二的字符串 区分 。

// common.ts
import { BaseActionCmd } from '@tencent/common-postmessage';
// BaseActionCmd 中定义了十数个字段, 可用于业务侧的父子页通用行为标识, 当此字段数量不够用时, 该包支持自定义枚举


// 建议:优先使用 BaseActionCmd, 根据业务需求赋予其命令不同含义 。业务侧自定义枚举采用独一无二的字符串区分。

// 业务侧自定义枚举: 子页面传递给父页面的事件命令
enum ChildPostAction {
  /** 更新父页面的top值 */
  UPDATE_PARENT_TOP = 'UPDATE_PARENT_TOP',
}

// 业务侧自定义枚举: 父页面传递给子页面的事件命令
enum ParentPostAction {
  /** 更新子页面的bottom值 */
  UPDATE_CHILD_BOTTOM = 'UPDATE_CHILD_BOTTOM',
}

在父页面中, 如下使用:

import { getParentWindowCtrl, ParentWindowProps } from '@/common-postmessage';
import { ChildPostAction, ParentPostAction } from './common';

// 业务侧自定义: 父页面通信事件集合 (父页面根据收到的子页面的数据, 触发对应的父页面事件)
const PARENT_ACTION_MAP = {
  [ChildPostAction.UPDATE_PARENT_TOP]: () => {},
};

// 业务侧自定义: 父页面通信事件集合 (父页面根据收到的子页面的数据, 触发对应的父页面事件)
const PARENT_ACTION_MAP = {
  [ChildPostAction.UPDATE_PARENT_TOP]: () => {},
};

/** 
 * 页面通信自定义业务配置
 * @desc 可选填
 * @desc 可以放一些父页面专用的配置项
 */
const INIT_CUSTOMIZED_MAP = {
  /** 初始化数据 */
  awaitInitData: '',
  /** 发送数据 */
  awaitPostData: '',
};

const configProps: ParentWindowProps<ChildPostAction> = {
  pageActionMap: PARENT_ACTION_MAP,
  customizedMap: INIT_CUSTOMIZED_MAP,
}

// App 页面初始化时: vue-mounted, react-didmount
getParentWindowCtrl<ChildPostAction, ParentPostAction>(configProps);

// 在别的页面调用时, 可无需挂在到 store 中, 直接调用即可获得单例
// 使用时一定要在 初始化 调用结束之后!!!
const parentWindow = getParentWindowCtrl();

在子页面中, 如下使用:

import { getChildWindowCtrl, ChildWindowProps } from '@/common-postmessage';
import { ChildPostAction, ParentPostAction } from './common';

// 业务侧自定义: 子页面通信事件集合 (子页面根据收到的父页面的数据, 触发对应的子页面事件)
const CHILD_ACTION_MAP = {
  [ParentPostAction.UPDATE_CHILD_BOTTOM]: () => {},
};

/** 
 * 页面通信自定义业务配置
 * @desc 可选填
 * @desc 可以放一些子页面专用的配置项
 */
const INIT_CUSTOMIZED_MAP = {
  /** 通知父页面, 当前scrolltop变化 */
  postScrollTopCmd: () => {},
};

const configProps: ChildWindowProps<ParentPostAction> = {
  pageActionMap: CHILD_ACTION_MAP,
  customizedMap: INIT_CUSTOMIZED_MAP,
}

// App 页面初始化时: vue-mounted, react-didmount
getChildWindowCtrl<ChildPostAction, ParentPostAction>(configProps);

// 在别的页面调用时, 可无需挂在到 store 中, 直接调用即可获得单例
// 使用时一定要在 初始化 调用结束之后!!!
const childWindow = getChildWindowCtrl();

4.源码实现

type

child

parent