New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

postmessage-duplex

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

postmessage-duplex

Lightweight duplex communication library based on postMessage API. Supports iframe and Service Worker.

latest
Source
npmnpm
Version
1.2.0
Version published
Maintainers
1
Created
Source

postmessage-duplex

npm version license TypeScript gzip size

A lightweight, type-safe duplex communication library based on postMessage API. Supports both iframe and Service Worker communication scenarios.

基于 postMessage API 的轻量级、类型安全的双工通讯库。支持 iframeService Worker 两种通讯场景。

📖 在线文档 / Documentation · 🚀 快速开始 · 🎮 在线演示 · 📚 API 参考

Why postmessage-duplex? / 为什么选择它?

FeatureNative postMessagepostmessage-duplex
请求-响应模式❌ 需要手动实现✅ 内置支持
Promise 支持❌ 回调模式✅ async/await
超时处理❌ 需要手动实现✅ 自动超时
消息队列❌ 需要手动实现✅ 自动队列
类型安全❌ any 类型✅ 完整类型定义
Service Worker❌ API 不同✅ 统一接口

Features / 特性

  • 🔄 Duplex Communication - 完整的双向消息传递,支持请求-响应模式
  • 🎯 Type Safe - TypeScript 编写,完整类型定义
  • 📦 Lightweight - 零依赖,gzip 后 ~8KB
  • ⏱️ Timeout Handling - 内置请求超时机制,默认 5 秒
  • 📋 Message Queue - 连接就绪前自动缓存消息
  • 🔌 Multiple Scenarios - 统一的 iframe 和 Service Worker 通讯接口
  • 🔍 Debug Friendly - 内置消息追踪,方便调试

Installation / 安装

npm install postmessage-duplex
# or
yarn add postmessage-duplex
# or
pnpm add postmessage-duplex

CDN:

<script src="https://unpkg.com/postmessage-duplex/dist/index.umd.js"></script>
<script>
  const { IframeChannel, ServiceWorkerChannel } = window.PostMessageChannel
</script>

Quick Start / 快速开始

Iframe Communication / Iframe 通讯

┌─────────────────────────────────────────────────────────────┐
│  Parent Page (父页面)                                        │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  const channel = new IframeChannel(iframe)          │    │
│  │  channel.publish('getData', {id: 1})  ──────────────│────│───┐
│  │  channel.subscribe('notify', handler) <─────────────│────│───│─┐
│  └─────────────────────────────────────────────────────┘    │   │ │
│                          ▲                                   │   │ │
│                          │ iframe                           │   │ │
│  ┌───────────────────────┴─────────────────────────────┐    │   │ │
│  │  Child Page (子页面)                                 │    │   │ │
│  │  const channel = new IframeChannel(parentOrigin)    │    │   │ │
│  │  channel.subscribe('getData', handler)  <───────────│────│───┘ │
│  │  channel.publish('notify', data)  ──────────────────│────│─────┘
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

Parent Page / 父页面:

import { IframeChannel } from 'postmessage-duplex'

const iframe = document.getElementById('my-iframe') as HTMLIFrameElement
const channel = new IframeChannel(iframe)

// 发送消息并等待响应
const response = await channel.publish('getUserInfo', { userId: 123 })
console.log('User info:', response.data)

// 监听子页面消息
channel.subscribe('notification', ({ data }) => {
  console.log('Received:', data)
  return { received: true }  // 返回响应
})

Child Page / 子页面:

import { IframeChannel } from 'postmessage-duplex'

// 传入父页面的 origin
const channel = new IframeChannel('https://parent-domain.com')

// 监听父页面消息
channel.subscribe('getUserInfo', async ({ data }) => {
  const user = await fetchUser(data.userId)
  return user  // 返回给父页面
})

// 向父页面发送消息
channel.publish('notification', { type: 'ready' })

Service Worker Communication / Service Worker 通讯

Page Side / 页面端:

import { ServiceWorkerChannel } from 'postmessage-duplex'

// 等待 Service Worker 就绪
const channel = await ServiceWorkerChannel.createFromPage()

// 发送消息到 Service Worker
const response = await channel.publish('fetchData', { url: '/api/data' })
console.log('Data:', response.data)

// 监听 Service Worker 推送
channel.subscribe('push', ({ data }) => {
  showNotification(data)
})

Service Worker Side (推荐使用全局路由):

// sw.js
import { ServiceWorkerChannel } from 'postmessage-duplex'

const channels = new Map()

// 共享的消息处理器
const subscribeMap = {
  fetchData: async ({ data }) => {
    const response = await fetch(data.url)
    return await response.json()
  }
}

// 启用全局路由 - 自动处理 SW 重启后的消息
ServiceWorkerChannel.enableGlobalRouting((clientId, event) => {
  const channel = ServiceWorkerChannel.createFromWorker(clientId, { subscribeMap })
  channels.set(clientId, channel)
  channel.handleMessage(event) // 处理当前消息
})

💡 全局路由的优势: 当 Service Worker 重启后,客户端的消息可以被自动处理,无需重新连接。详见 Service Worker 指南

Framework Integration / 框架集成

Vue 3

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { IframeChannel } from 'postmessage-duplex'

const iframeRef = ref<HTMLIFrameElement>()
let channel: IframeChannel

onMounted(() => {
  channel = new IframeChannel(iframeRef.value!)
  
  channel.subscribe('childEvent', ({ data }) => {
    console.log('From child:', data)
    return { ok: true }
  })
})

onUnmounted(() => {
  channel?.destroy()
})

const sendToChild = async () => {
  const res = await channel.publish('parentEvent', { msg: 'hello' })
  console.log('Response:', res)
}
</script>

<template>
  <iframe ref="iframeRef" src="./child.html" />
  <button @click="sendToChild">Send</button>
</template>

React

import { useEffect, useRef } from 'react'
import { IframeChannel } from 'postmessage-duplex'

function ParentComponent() {
  const iframeRef = useRef<HTMLIFrameElement>(null)
  const channelRef = useRef<IframeChannel>()

  useEffect(() => {
    if (iframeRef.current) {
      channelRef.current = new IframeChannel(iframeRef.current)
      
      channelRef.current.subscribe('childEvent', ({ data }) => {
        console.log('From child:', data)
        return { ok: true }
      })
    }
    
    return () => channelRef.current?.destroy()
  }, [])

  const sendToChild = async () => {
    const res = await channelRef.current?.publish('parentEvent', { msg: 'hello' })
    console.log('Response:', res)
  }

  return (
    <>
      <iframe ref={iframeRef} src="./child.html" />
      <button onClick={sendToChild}>Send</button>
    </>
  )
}

API Reference / API 文档

IframeChannel

// 创建通道
const channel = new IframeChannel(target, options?)

// target: 
//   - 父页面传入: HTMLIFrameElement
//   - 子页面传入: string (父页面 origin,如 'https://parent.com')

// options:
interface ChannelOption {
  timeout?: number                           // 超时时间,默认 5000ms
  log?: Console                              // 自定义日志
  subscribeMap?: Record<string, Function>    // 预定义订阅
}
MethodReturnDescription
publish(cmdname, data?)Promise<PostResponse>发送消息并等待响应
subscribe(cmdname, callback)this订阅消息
unSubscribe(cmdname)this取消订阅
destroy()void销毁通道
getTargetOrigin()string获取目标 origin
getTargetUrl()string获取目标 URL
PropertyTypeDescription
isReadyboolean通道是否就绪
isSonboolean是否为子页面

ServiceWorkerChannel

// 页面端 - 推荐方式
const channel = await ServiceWorkerChannel.createFromPage(options?)

// 页面端 - 手动传入 ServiceWorker
const channel = new ServiceWorkerChannel(navigator.serviceWorker.controller, options?)

// Worker 端 - 从事件创建
const channel = ServiceWorkerChannel.createFromEvent(event, options?)

// Worker 端 - 从 clientId 创建
const channel = ServiceWorkerChannel.createFromWorker(clientId, options?)

PostResponse

interface PostResponse {
  requestId?: string      // 请求 ID
  ret: ReturnCode         // 返回码
  data?: any              // 响应数据
  msg?: string            // 错误信息
  time?: number           // 时间戳
}

ReturnCode / 返回码

import { ReturnCode } from 'postmessage-duplex'

ReturnCode.Success               // 0: 成功
ReturnCode.ReceiverCallbackError // -1: 接收方回调错误
ReturnCode.SendCallbackError     // -2: 发送方回调错误
ReturnCode.NoSubscribe           // -3: 未订阅该事件
ReturnCode.TimeOut               // -99: 请求超时

Advanced Usage / 高级用法

Error Handling / 错误处理

const response = await channel.publish('getData', { id: 1 })

if (response.ret === ReturnCode.Success) {
  console.log('Success:', response.data)
} else if (response.ret === ReturnCode.TimeOut) {
  console.error('Request timeout')
} else if (response.ret === ReturnCode.NoSubscribe) {
  console.error('Event not subscribed on the other side')
} else {
  console.error('Error:', response.msg)
}

Custom Timeout / 自定义超时

const channel = new IframeChannel(iframe, {
  timeout: 10000  // 10 秒超时
})

Custom Logger / 自定义日志

const channel = new IframeChannel(iframe, {
  log: {
    log: (...args) => console.log('[Channel]', ...args),
    warn: (...args) => console.warn('[Channel]', ...args),
    error: (...args) => console.error('[Channel]', ...args)
  }
})

Pre-defined Subscribers / 预定义订阅

const channel = new IframeChannel(iframe, {
  subscribeMap: {
    'ping': () => ({ pong: true, time: Date.now() }),
    'getVersion': () => ({ version: '1.0.0' })
  }
})

Debug Tools / 调试工具

import { enableDebugger } from 'postmessage-duplex'

// 在开发环境启用调试器
if (process.env.NODE_ENV === 'development') {
  enableDebugger()
}

// 然后在浏览器控制台使用:
__POSTMESSAGE_DUPLEX__.debug.help()           // 显示帮助
__POSTMESSAGE_DUPLEX__.debug.getChannels()    // 查看所有通道
__POSTMESSAGE_DUPLEX__.debug.getHistory()     // 查看消息历史
__POSTMESSAGE_DUPLEX__.debug.enableLiveLog(true)  // 开启实时日志
__POSTMESSAGE_DUPLEX__.debug.getStats()       // 查看统计信息
__POSTMESSAGE_DUPLEX__.debug.exportReport()   // 导出调试报告

Browser Compatibility / 浏览器兼容性

BrowserIframeService Worker
Chrome✅ 4+✅ 40+
Firefox✅ 3+✅ 44+
Safari✅ 4+✅ 11.1+
Edge✅ 12+✅ 17+
IE✅ 8+

FAQ / 常见问题

Q: 子页面刷新后连接会断开吗?
A: 不会。通道会自动处理子页面的重新加载,父页面无需重新创建通道。

Q: 可以同时与多个 iframe 通讯吗?
A: 可以。为每个 iframe 创建独立的 IframeChannel 实例即可。

Q: 跨域 iframe 可以通讯吗?
A: 可以,但子页面需要正确配置父页面的 origin。

Q: Service Worker 端如何使用这个库?
A: Service Worker 不支持直接 import,需要使用内联实现或打包工具。参考 demo 中的 sw.js 示例。

Development / 开发

# 安装依赖
npm install

# 启动开发服务器
npm run dev

# 访问演示页面
open http://localhost:7100/demo/
ScriptDescription
npm run dev构建并启动开发服务器
npm run build构建生产版本
npm run build:watch监听模式构建
npm test运行测试
npm run test:coverage测试覆盖率

Project Structure / 项目结构

src/
├── index.ts           # 入口,统一导出
├── base-channel.ts    # 抽象基类
├── iframe-channel.ts  # Iframe 通道
├── sw-channel.ts      # Service Worker 通道
├── interface.ts       # 类型定义
└── trace.ts           # 消息追踪

demo/
├── iframe/            # Iframe 示例
├── service-worker/    # SW 示例
└── debugger/          # 调试工具

Documentation / 更多文档

完整文档请访问:https://ljquan.github.io/postmessage-duplex/

文档链接
快速开始Getting Started
Iframe 通讯指南Iframe Communication
Service Worker 指南Service Worker
TypeScript 支持TypeScript
调试指南Debugging
API 参考API Reference
在线演示Playground

Changelog / 更新日志

See CHANGELOG.md

Contributing / 贡献

欢迎提交 Issue 和 Pull Request!

License / 许可证

MIT

Made with ❤️ by liquidliang

Keywords

postmessage

FAQs

Package last updated on 06 Feb 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts