npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

h5-iframe-ipc

v1.0.5

Published

一个轻量级的H5与iframe通信SDK,支持双向消息传递和事件监听

Readme

h5-iframe-ipc

npm version license downloads

一个轻量级、类型安全的 H5 与 iframe 双向通信 SDK

✨ 特性

  • 🚀 轻量级:零依赖,gzip 后仅 ~2KB
  • 🔒 类型安全:完整的 TypeScript 类型定义
  • 📡 双向通信:支持父页面 ↔ 子页面(H5)双向消息传递
  • 🎯 Promise 支持:invoke 方法支持 Promise,轻松处理异步操作
  • 📢 事件系统:内置事件监听和发射机制(on/off/emit)
  • 🧹 资源管理:提供 destroy 方法,避免内存泄漏
  • 🔧 简单易用:API 设计简洁直观,5 分钟快速上手

📦 安装

# npm
npm install h5-iframe-ipc

# yarn
yarn add h5-iframe-ipc

# pnpm
pnpm add h5-iframe-ipc

🚀 快速开始

H5 子页面(在 iframe 内运行)

H5 页面使用 IPCH5 类,运行在 iframe 内部。

import { IPCH5 } from 'h5-iframe-ipc';

// 创建 IPC 实例
const ipc = new IPCH5();

// 注册方法供父页面调用
ipc.handle('getUserInfo', (params) => {
  return {
    name: '张三',
    age: 25,
    email: '[email protected]'
  };
});

// 监听来自父页面的事件
ipc.on('parentReady', (data) => {
  console.log('父页面已就绪:', data);
});

// 向父页面发送事件
ipc.emit('h5Loaded', {
  timestamp: Date.now(),
  version: '1.0.0'
});

iframe 父页面(包含 iframe 标签)

父页面使用 IPCIframe 类,需要传入 iframe 的 contentWindow。

import { IPCIframe } from 'h5-iframe-ipc';

// 获取 iframe 元素
const iframe = document.getElementById('myIframe');

// 创建 IPC 实例(传入 iframe 的 contentWindow)
const ipc = new IPCIframe(iframe.contentWindow);

// 调用 H5 注册的方法(支持 async/await)
async function fetchUserInfo() {
  try {
    const userInfo = await ipc.invoke('getUserInfo', {
      userId: '12345'
    });
    console.log('用户信息:', userInfo);
  } catch (error) {
    console.error('获取用户信息失败:', error);
  }
}

// 监听来自 H5 的事件
ipc.on('h5Loaded', (data) => {
  console.log('H5 页面已加载:', data);
});

// 向 H5 发送事件
ipc.emit('parentReady', {
  status: 'ready',
  version: '1.0.0'
});

📖 API 文档

IPCH5

H5 子页面(运行在 iframe 内)使用的 IPC 实例。

构造函数

const ipc = new IPCH5();

无需传参,自动通过 window.parent 与父页面通信。

方法

handle(channel: string, callback: Function): void

注册一个方法,供父页面通过 invoke 调用。

参数:

  • channel - 方法通道名称
  • callback - 回调函数,可以返回任意值或 Promise

示例:

// 同步返回
ipc.handle('getConfig', (params) => {
  return { theme: 'dark', language: 'zh-CN' };
});

// 异步返回(Promise)
ipc.handle('fetchData', async (params) => {
  const response = await fetch('/api/data');
  return response.json();
});
on(eventName: string, callback: Function): void

监听来自父页面的事件。

参数:

  • eventName - 事件名称
  • callback - 事件处理函数

示例:

ipc.on('themeChanged', (data) => {
  console.log('主题已切换:', data);
});
off(eventName: string, callback?: Function): void

移除事件监听。

参数:

  • eventName - 事件名称
  • callback - (可选)要移除的具体回调函数,不传则移除该事件的所有监听

示例:

const handler = (data) => console.log(data);

// 添加监听
ipc.on('myEvent', handler);

// 移除特定监听
ipc.off('myEvent', handler);

// 移除该事件的所有监听
ipc.off('myEvent');
emit(eventName: string, params: any): void

向父页面发送事件。

参数:

  • eventName - 事件名称
  • params - 事件数据

示例:

ipc.emit('userAction', {
  type: 'click',
  target: 'submitButton'
});
destroy(): void

销毁 IPC 实例,清理所有监听器和处理器。

示例:

// 页面卸载时清理
window.addEventListener('beforeunload', () => {
  ipc.destroy();
});

IPCIframe

iframe 父页面使用的 IPC 实例。

构造函数

const iframe = document.getElementById('myIframe');
const ipc = new IPCIframe(iframe.contentWindow);

参数:

  • win - iframe 的 contentWindow 对象

示例:

// 方式一:通过 getElementById
const iframe = document.getElementById('myIframe');
const ipc = new IPCIframe(iframe.contentWindow);

// 方式二:通过 querySelector
const iframe = document.querySelector('#myIframe');
const ipc = new IPCIframe(iframe.contentWindow);

// 方式三:监听 iframe 加载后初始化
const iframe = document.getElementById('myIframe');
iframe.onload = () => {
  const ipc = new IPCIframe(iframe.contentWindow);
};

方法

invoke(channel: string, params: any): Promise<any>

调用 H5 注册的方法,返回 Promise。

参数:

  • channel - 方法通道名称
  • params - 传递的参数

返回:

  • Promise<any> - 返回 H5 处理函数的结果

示例:

// 基本用法
const result = await ipc.invoke('getData', { id: 123 });

// 错误处理
try {
  const user = await ipc.invoke('getUserInfo', { userId: '456' });
  console.log(user);
} catch (error) {
  console.error('调用失败:', error);
}
on(eventName: string, callback: Function): void

监听来自 H5 的事件。

参数:

  • eventName - 事件名称
  • callback - 事件处理函数

示例:

ipc.on('h5Ready', (data) => {
  console.log('H5 已就绪:', data);
});
off(eventName: string, callback?: Function): void

移除事件监听。

参数:

  • eventName - 事件名称
  • callback - (可选)要移除的具体回调函数

示例:

const handler = (data) => console.log(data);

// 添加监听
ipc.on('update', handler);

// 移除特定监听
ipc.off('update', handler);

// 移除所有监听
ipc.off('update');
emit(eventName: string, params: any): void

向 H5 发送事件。

参数:

  • eventName - 事件名称
  • params - 事件数据

示例:

ipc.emit('notification', {
  type: 'success',
  message: '操作成功'
});
destroy(): void

销毁 IPC 实例,清理所有监听器和待处理的 Promise。

示例:

// 组件卸载时清理
componentWillUnmount() {
  ipc.destroy();
}

🎯 完整示例

场景:用户登录流程

H5 子页面(login.html - 运行在 iframe 内)

import { IPCH5 } from 'h5-iframe-ipc';

class LoginPage {
  constructor() {
    this.ipc = new IPCH5();
    this.init();
  }

  init() {
    // 注册登录方法供父页面调用
    this.ipc.handle('login', async (credentials) => {
      const { username, password } = credentials;
      
      // 调用后端 API
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
      });

      if (!response.ok) {
        throw new Error('登录失败');
      }

      const result = await response.json();
      
      // 通知父页面登录成功
      this.ipc.emit('loginSuccess', result.user);
      
      return result;
    });

    // 注册获取配置方法
    this.ipc.handle('getLoginConfig', () => {
      return {
        enableSocialLogin: true,
        supportedProviders: ['wechat', 'alipay'],
        locale: 'zh-CN'
      };
    });

    // 监听父页面的主题变化
    this.ipc.on('themeChanged', (theme) => {
      console.log('主题已切换:', theme);
      this.applyTheme(theme);
    });

    // 监听初始数据
    this.ipc.on('initData', (data) => {
      console.log('接收到初始数据:', data);
      this.applyTheme(data.theme);
    });

    // 通知父页面 H5 已就绪
    this.ipc.emit('h5Ready', {
      version: '1.0.0',
      timestamp: Date.now()
    });
  }

  applyTheme(theme) {
    document.body.setAttribute('data-theme', theme);
  }

  destroy() {
    this.ipc.destroy();
  }
}

// 创建实例
const loginPage = new LoginPage();

// 绑定表单提交
document.querySelector('#loginForm')?.addEventListener('submit', async (e) => {
  e.preventDefault();
  const username = document.querySelector('#username').value;
  const password = document.querySelector('#password').value;
  
  // 可以在 H5 内部直接调用注册的方法,也可以等父页面调用
  try {
    const result = await loginPage.ipc.invoke('login', { username, password });
    console.log('登录成功:', result);
  } catch (error) {
    console.error('登录失败:', error);
  }
});

iframe 父页面(index.html - 包含 iframe)

<!DOCTYPE html>
<html>
<head>
  <title>父页面</title>
</head>
<body>
  <!-- iframe 嵌入 H5 页面 -->
  <iframe id="loginIframe" src="login.html" width="100%" height="600px"></iframe>
  
  <script type="module">
    import { IPCIframe } from 'h5-iframe-ipc';

    class ParentPage {
      constructor() {
        this.iframe = document.getElementById('loginIframe');
        this.initIframe();
      }

      initIframe() {
        // 等待 iframe 加载完成
        this.iframe.onload = () => {
          // 创建 IPC 实例
          this.ipc = new IPCIframe(this.iframe.contentWindow);
          this.init();
        };
      }

      async init() {
        // 获取 H5 的配置
        const config = await this.ipc.invoke('getLoginConfig', {});
        console.log('H5 配置:', config);

        // 监听 H5 就绪事件
        this.ipc.on('h5Ready', (data) => {
          console.log('H5 已就绪:', data);
          
          // 向 H5 发送初始数据
          this.ipc.emit('initData', {
            theme: 'light',
            apiUrl: 'https://api.example.com'
          });
        });

        // 监听登录成功事件
        this.ipc.on('loginSuccess', (user) => {
          console.log('用户登录成功:', user);
          this.updateUserInfo(user);
        });

        // 可以从父页面主动调用 H5 的登录方法
        this.bindParentLoginButton();
      }

      bindParentLoginButton() {
        document.getElementById('parentLoginBtn')?.addEventListener('click', async () => {
          try {
            const result = await this.ipc.invoke('login', {
              username: 'testuser',
              password: 'password123'
            });
            console.log('通过父页面调用登录成功:', result);
          } catch (error) {
            console.error('登录失败:', error);
          }
        });
      }

      updateUserInfo(user) {
        // 更新父页面 UI
        document.getElementById('username').textContent = user.name;
      }

      changeTheme(theme) {
        // 通知 H5 切换主题
        this.ipc.emit('themeChanged', theme);
      }

      destroy() {
        this.ipc.destroy();
      }
    }

    // 创建实例
    const parentPage = new ParentPage();
  </script>
</body>
</html>

🔧 TypeScript 支持

本 SDK 使用 TypeScript 编写,提供完整的类型定义。

import { IPCH5, IPCIframe, IPCType } from 'h5-iframe-ipc';

// H5 子页面
const h5Ipc: IPCH5 = new IPCH5();

// 自定义类型
interface UserInfo {
  name: string;
  age: number;
  email: string;
}

h5Ipc.handle('getUserInfo', async (): Promise<UserInfo> => {
  return {
    name: '张三',
    age: 25,
    email: '[email protected]'
  };
});

// iframe 父页面
const iframe = document.getElementById('myIframe') as HTMLIFrameElement;
const parentIpc: IPCIframe = new IPCIframe(iframe.contentWindow!);

// 调用时可以指定返回类型
const userInfo = await parentIpc.invoke<UserInfo>('getUserInfo', {});
console.log(userInfo.name); // TypeScript 会有类型提示

⚠️ 注意事项

1. iframe 加载时机

必须等待 iframe 加载完成后再创建 IPCIframe 实例:

// ✅ 正确
const iframe = document.getElementById('myIframe');
iframe.onload = () => {
  const ipc = new IPCIframe(iframe.contentWindow);
};

// ❌ 错误 - iframe 可能还未加载完成
const iframe = document.getElementById('myIframe');
const ipc = new IPCIframe(iframe.contentWindow); // contentWindow 可能为 null

2. 内存泄漏

在组件或页面销毁时,务必调用 destroy() 方法清理资源:

// React 示例
useEffect(() => {
  const iframe = document.getElementById('myIframe');
  let ipc;
  
  iframe.onload = () => {
    ipc = new IPCIframe(iframe.contentWindow);
  };
  
  return () => {
    ipc?.destroy(); // 清理
  };
}, []);

// Vue 3 示例
import { onMounted, onUnmounted } from 'vue';

let ipc = null;

onMounted(() => {
  const iframe = document.getElementById('myIframe');
  iframe.onload = () => {
    ipc = new IPCIframe(iframe.contentWindow);
  };
});

onUnmounted(() => {
  ipc?.destroy();
});

3. 错误处理

invoke 方法返回 Promise,务必处理可能的错误:

try {
  const result = await ipc.invoke('someMethod', params);
  // 处理成功结果
} catch (error) {
  console.error('调用失败:', error);
  // 错误处理逻辑
}

4. 事件监听器管理

避免重复添加相同的监听器,必要时使用 off 方法移除:

const handler = (data) => console.log(data);

// 添加前先移除(确保不会重复添加)
ipc.off('myEvent', handler);
ipc.on('myEvent', handler);

5. IPCIframe 构造函数参数

创建 IPCIframe 实例时,必须传入 iframe 的 contentWindow:

// ✅ 正确
const iframe = document.getElementById('myIframe');
const ipc = new IPCIframe(iframe.contentWindow);

// ❌ 错误
const ipc = new IPCIframe(window); // 这是错误的
const ipc = new IPCIframe(window.parent); // 这也是错误的

🏗️ 架构说明

┌─────────────────────────────────────┐
│      iframe 父页面 (Parent)          │
│                                     │
│   import { IPCIframe }              │
│   const ipc = new IPCIframe(        │
│     iframe.contentWindow            │
│   )                                 │
│                                     │
│   ┌─────────────────────────────┐   │
│   │  <iframe src="h5.html">     │   │
│   │                             │   │
│   │  H5 子页面 (Child)           │   │
│   │                             │   │
│   │  import { IPCH5 }           │   │
│   │  const ipc = new IPCH5()    │   │
│   │                             │   │
│   └─────────────────────────────┘   │
│                                     │
└─────────────────────────────────────┘

通信流程:
父页面 → ipc.invoke() → H5.handle()
父页面 ← H5.emit() ← H5 子页面
父页面 → ipc.emit() → H5.on()
父页面 ← ipc.on() ← H5.emit()

🌐 浏览器兼容性

支持所有支持 postMessage API 的现代浏览器:

  • ✅ Chrome / Edge (Chromium) >= 60
  • ✅ Firefox >= 55
  • ✅ Safari >= 11
  • ✅ iOS Safari >= 11
  • ✅ Android WebView >= 60

📚 相关文档

🐛 问题反馈

如果你发现任何问题或有改进建议,欢迎提交 Issue:

🤝 贡献

欢迎提交 Pull Request!在提交之前,请确保:

  1. 代码通过 TypeScript 编译
  2. 遵循项目代码风格
  3. 添加必要的注释

📄 许可证

MIT © hehongyu

🔗 相关链接


如果这个项目对你有帮助,欢迎 ⭐ Star 支持一下!