简介
近期在将 XD 等设计资源迁移到即时设计时,有些功能在即时设计上需要使用插件来实现。
在开发插件时,即时设计官方的宿主与插件UI页面,使用 postMessage 通信(经过了官方的一定的封装),使用方法与原生 js 中的 postMessage 基本一致。
由于 postMessage 特性,双方通信需通过类似发布与监听的方式进行开发,虽然通信简单,但有时候不太方便。
本文提供一个用于开发时的辅助套件,方便宿主与 iframe 通信。
同时本项目,也可做为使用 React 进行即时设计插件开发的基础模板。
官方方案
UI 向宿主发送消息
parent.postMessage({
pluginMessage: {type: 'xxx', msg: 'hi host, from ui'}
}, '*')
宿主接收 UI 的消息
jsDesign.ui.onmessage = (message) => {
console.log("收到来自前端的消息", message)
switch(message.type) {
...区分不同消息,进行不同处理...
}
}
宿主向 UI 发送消息
jsDesign.ui.postMessage({type: 'xxx', msg: 'hi ui, from host'} )
UI 接收宿主的消息
window.onmessage = (event) => {
console.log("收到来自主线程脚本的消息", event.data.pluginMessage)
if(message.type === 'changeColor') {
...区分不同消息,进行不同处理...
}
}
可以看出使用方法与原生 postMessage 基本一致。
这里有两个问题:
-
通过重置 window.onmessage 和 jsDesign.ui.onmessage 来达到消息监听的目的。导致所有消息通过一个方法接收,开发者需要通过 switch/if 等对消息进行区分。(官方也有on/off等类似于addEventListener的方法,经测试,消息会被重复收到多次,原因不明,未敢实际用于开发)
-
不能区分每次通信。 比如多次调用一个add方法:
postMessage({type: 'add', args: [1, 2]})
postMessage({type: 'add', args: [3, 4]})
宿主收到消息后,进行相加然后返回结果,但是结果并不是同步返回,而是再次通过postMessage。即:
jsDesign.ui.postMessage({type: 'add-response', result: 3} )
jsDesign.ui.postMessage({type: 'add-response', result: 7} )
除非手动在每个消息中传递一个唯一id用于区分,否则 ui 收到结果后并不知道3是1+2的结果还是3+4的结果
解决方案
使用此封装好的通信工具,在宿主与 UI 间进行"同步通信"。
ui 调用 host 中的方法
首先在 host 中提供一个 createRectangle 方法:
import {createUiBridge} from '@jsdkit/bridge';
const ui = createUiBridge();
// 定义一个方法,供 ui 环境使用
ui.createRectangle = async (count) => {
console.log('ui 传递的参数:', count); // 123
// 使用 return 将值直接传递给 ui
return true;
};
jsDesign.showUI(__html__);
在 ui 环境中,直接调用宿主中的 createRectangle 方法:
import {createHostBridge} from '@jsdkit/bridge';
const host = createHostBridge('你的插件id');
const res = await host.createRectangle(123);
console.log(res) // res 即为 host 中 createRectangle 方法的返回值 (true)
host 调用 ui 中的方法
首先在 ui 中提供一个 changeButtonColor 方法:
import {createHostBridge} from '@jsdkit/bridge';
const host = createHostBridge('你的插件id');
// 定义一个方法供 host 调用
host.changeButtonColor = (color: string) => {
// 收到 color 参数
document.getElementById('btn').style.backgroundColor = color;
};
在 host 环境中,直接调用 ui 中的 changeButtonColor 方法:
// 调用 ui 方法
import {createUiBridge} from '@jsdkit/bridge';
const ui = createUiBridge();
ui.changeButtonColor('#f00'); // 如果有返回值,也可以使用 await 来接收
jsDesign.showUI(__html__);
TypeScript 支持
您只需要对提供的方法进行声明,并在 createBridge 时传入泛型声明即可:
示例:
// interfaces.ts ,此文件仅为 ts 定义文件,会被 host 和 ui 环境共用类型提示
export interface Bridge {
/** 创建矩形 */
createRectangle(count: number): Promise<boolean>;
/** 改变按钮颜色 */
changeButtonColor(color: string): void;
}
在 ui 中使用:
const host = createHostBridge<Bridge>('你的插件id');
之后输入 host.,您的编辑器即可自动提示。
在 host 中使用:
const ui = createUiBridge<Bridge>();
之后输入 ui.,您的编辑器即可自动提示。