protocol-bridge
具有事件名和事件参数类型提示的 Emitter 类型的通信协议,具体包含:
- 基座应用与h5应用之间进行postMessage通信;
- 在Web应用中进行跨页签通信。
一、安装
npm i protocol-bridge
二、使用
2.1 基座应用base-app中
import { createProtocolContext } from 'protocol-bridge';
type IDemoProtocolEventMap =
| ['container.height.resize', 'number', 'undefined']
| ['selectDate', 'string', 'string', 'undefined']
| ['showLoading', 'undefined', 'undefined']
| ['user.login', 'string', 'boolean', 'undefined']
| ['user.logout', 'number', 'undefined', 'undefined']
| ['user.profile.update', 'undefined', 'string', 'undefined'];
export const protocolCtx = createProtocolContext<IDemoProtocolEventMap>();
IDemoProtocolEventMap 必须受到 IProtocolEvent 的约束,即:
function createProtocolContext<EV extends IProtocolEvent>() {}
其中
type IJsTypeMap = {
string: string;
boolean: boolean;
number: number;
object: object;
undefined: undefined;
};
type IProtocolEvent =
| [string, keyof IJsTypeMap, keyof IJsTypeMap, keyof IJsTypeMap]
| [string, keyof IJsTypeMap, keyof IJsTypeMap];
<template>
<iframe src="http://localhost:6173/" @load="ev => protocolCtx.onContainerLoaded(createWebChannelPlugin(ev))"></iframe>
</template>
<script setup lang="ts">
import { createWebChannelPlugin } from 'protocol-bridge';
import { protocolCtx } from './utils/protocolBridge';
onMounted(() => {
protocolCtx.on('selectDate', (str, successCallback, errorCallback) => {
if (Math.random() > 0.5) {
const res = `${str ?? ''}-${Math.random()}`;
successCallback(res);
} else {
errorCallback(undefined);
}
});
});
</script>
2.2 H5应用h5-app中
import { useProtocolContext } from 'protocol-bridge';
type IDemoProtocolEventMap =
| ['container.height.resize', 'number', 'undefined']
| ['selectDate', 'string', 'string', 'undefined']
| ['showLoading', 'undefined', 'undefined']
| ['user.login', 'string', 'boolean', 'undefined']
| ['user.logout', 'number', 'undefined', 'undefined']
| ['user.profile.update', 'undefined', 'string', 'undefined'];
export const protocolCtx = useProtocolContext<IDemoProtocolEventMap>();
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { protocolCtx } from "./utils/protocolBridge";
import "./index.css";
import App from "./App.tsx";
protocolCtx
.createProtocolBridge()
.then(() => {
console.log("已拿到port");
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
protocolCtx.emit('container.height.resize', entry.target.scrollHeight);
}
});
resizeObserver.observe(document.body);
})
.catch(() => {
console.log("连接失败");
});
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
import { useState } from 'react';
import { protocolCtx } from './utils/protocolBridge';
export default function IframeChannel() {
function handleSelectDate() {
protocolCtx
.emit('selectDate', '2025-12-1')
.then(data => {
console.log('handleSelectDate res data :>> ', data);
})
.catch(err => {
console.log('err :>> ', err);
});
}
return <button onClick={handleSelectDate}>点击给父组件发送selectDate事件</button>;
}
至此你完成了在web应用中接入h5应用的所有步骤
2.3 在Web应用中进行跨页签通信
import { createChannelEmitter } from 'protocol-bridge';
type IBroadcastChannelEventMap = ['setCount', 'number', 'number', 'undefined'];
export const channelEmitter = createChannelEmitter<IBroadcastChannelEventMap>();
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { channelEmitter } from '../utils/channelEmitter';
const count = ref(0);
onMounted(() => {
channelEmitter.on('setCount', (data, successCallback) => {
count.value = data;
successCallback(data);
});
});
onUnmounted(() => {
channelEmitter.off('setCount')
});
</script>
<script setup lang="ts">
import { ref } from 'vue';
import { channelEmitter } from '../utils/channelEmitter';
const count = ref(0);
function onCountClick() {
channelEmitter
.emit('setCount', count.value)
.then(res => {
count.value = res + 1;
})
.catch(err => {
console.log('🚀 ~ BroadcastChannelChild.vue:20 ~ onCountClick ~ err:', err);
});
}
</script>
三、自定义平台通信插件
如果基座应用是在harmony、平板或车机上,可以在接入时传入平台的通信方法插件,来进行通信。
例如在Harmony系统下
import { webview } from '@kit.ArkWeb';
export interface IChannelPlugin {
onMessageEvent(listener: (data: string) => void): void;
postMessageEvent(resMsg: string): void;
postContainerMessage(initPortMsg: string): void;
}
export function createArkWebChannelPlugin(controller: webview.WebviewController): IChannelPlugin {
const ports = controller.createWebMessagePorts();
const parentPort = ports[0];
return {
onMessageEvent(listener) {
parentPort.onMessageEvent(listener);
},
postMessageEvent(resMsg: string) {
parentPort.postMessageEvent(resMsg);
},
postContainerMessage(initPortMsg: string) {
controller.postMessage(initPortMsg, [ports[1]], '*');
},
};
}
import { createArkWebChannelPlugin } from "../utils/arkWebChannelPlugin";
import { protocolCtx } from '../utils/protocolBridge';
Web({ src: '', controller: this.controller })
.javaScriptAccess(true)
.fileAccess(true)
.domStorageAccess(true)
.onPageEnd(() => protocolCtx.onContainerLoaded(createArkWebChannelPlugin(this.controller)))