iframe 优雅通讯
最近开发了个项目,基座是VsCode插件,通过iframe集成了一个Vue3的子应用,子应用需要很频繁的与基座通讯。
我们可以通过 parent.postMessage
来向基座传递消息,通过 window.addEventListener('message', () => {})
来监听来自基座的消息。
但是在vue3的代码中写大量这样的代码就很不美观,我们可以将其封装成Promise
风格, 使用mitt
库来完成发布订阅。
我们在src下新建message文件夹, 并pnpm add mitt
开始吧
先定义ts类型
// ***********
// src/message/types.ts
// ***********
// 定义枚举 来存放与基座约定的eventId
export enum ON_MESSAGE_MAP {
API = 'TransformApis',
CONFIG = 'GetConfigToJson'
}
export enum EMIT_MESSAGE_MAP {
API = 'TransformApis',
CODE = 'GeneratorCode'
}
export type EMIT_MESSAGE_MAP_KEYS = keyof typeof EMIT_MESSAGE_MAP
export type ON_MESSAGE_MAP_KEYS = keyof typeof ON_MESSAGE_MAP
type MapTo<T extends string, U extends Record<T, unknown>> = {
[K in T]: U[K]
}
// 使用自定义Ts工具函数 将枚举key转为mitt需要使用到的类型
export type Emitter = MapTo<
ON_MESSAGE_MAP_KEYS,
{
API: VarTemplate.ContentOriginal[]
CONFIG: VarOneCodeConfig.OneCodeConfig
}
>
封装 window message 方法
// ***********
// src/message/on.ts
// ***********
import mitt from 'mitt'
import { ON_MESSAGE_MAP_KEYS, ON_MESSAGE_MAP } from './types'
import type { Emitter } from './types'
export const windowMessage = mitt<Emitter>()
const getMessageKey = (key: string) => {
let temp: ON_MESSAGE_MAP_KEYS | null = null
for (const [k, v] of Object.entries(ON_MESSAGE_MAP)) {
if (v === key) {
temp = <ON_MESSAGE_MAP_KEYS>k
break
}
}
return temp
}
// 格式化基座传递过来的数据 可以在这里做错误处理
const payloadParser: {
[P in ON_MESSAGE_MAP_KEYS]: (data: any) => Emitter[P]
} = {
API: (data) => {
if (!Array.isArray(data)) {
throw new Error('无效的 API message')
}
return data.map<VarTemplate.ContentOriginal>((v: any) => {
return {
content:
typeof v.content === 'string' ? v.content : JSON.stringify(v.content),
type: v.type as VarTemplate.FromType
}
})
},
CONFIG: () => {
return {
dataSource: [],
apiPackageResolver: {}
}
}
}
export const setupWindowMessage = () => {
window.addEventListener('message', (event: MessageEvent) => {
if (!event.data) return
const { data, eventId } = event.data
if (!eventId) return
const emitterKey = getMessageKey(eventId)
if (!emitterKey) return
windowMessage.emit(emitterKey, payloadParser[emitterKey](data))
})
}
封装向基座发消息的方法
// ***********
// src/message/emit.ts
// ***********
import {
EMIT_MESSAGE_MAP,
EMIT_MESSAGE_MAP_KEYS,
Emitter,
ON_MESSAGE_MAP_KEYS
} from './types'
import { windowMessage } from './on'
// 封装 postMessage
export const postMessage = <K extends EMIT_MESSAGE_MAP_KEYS, V = any>(
key: K,
data?: V
) => {
parent.postMessage(
{
eventId: EMIT_MESSAGE_MAP[key],
data: data || {}
},
'*'
)
}
// 我们将 emit与on封装在一起 并利用setTimeout做伪超时
const awaitMessage = <
K extends EMIT_MESSAGE_MAP_KEYS,
X extends ON_MESSAGE_MAP_KEYS,
V = any
>(
key: K,
onKey: X,
data?: V,
delay = 3000
) => {
return new Promise<Emitter[X]>((resolve, reject) => {
postMessage(key, data)
const timer = setTimeout(() => {
reject(new Error('timeout'))
windowMessage.off(onKey)
}, delay)
windowMessage.on(onKey, (data) => {
resolve(data)
clearTimeout(timer)
windowMessage.off(onKey)
})
})
}
// 子 -> 父组件 然后父 立刻响应 子
export const pushApiMessage = async () => {
return await awaitMessage('API', 'API', undefined, 1000 * 60 * 3)
}
// 子 -> 父 单向通讯
export const pushCodeMessage = (data: unknown) => {
postMessage('CODE', data)
}
最后在index.ts中导出
// ***********
// src/message/index.ts
// ***********
export * from './types'
export * from './on'
export * from './emit'
Usage
在main.ts中开始全局监听
// ***********
// src/main.ts
// ***********
import { createApp } from 'vue'
import { setupWindowMessage } from './message'
// ...
const app = createApp(App)
setupWindowMessage()
// ...
app.mount('#app')
在需要的地方
import { onMounted } from 'vue'
import { pushApiMessage, windowMessage } from '@/message'
onMounted(async () => {
const data = await pushApiMessage()
console.log(data)
// 就可以立即发送立即拿到基座返回的消息辣
// 或者
windowMessage.on('API', (event) => {
// 也可以收听到基座发来的消息辣
})
})
为之则易,不为则难。
本文作者:Code_You
本文链接:https://www.cnblogs.com/coderDemo/p/17458722.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步