iframe-bridge-sdk
Version:
Universal iframe communication SDK - unified package for host and guest applications
199 lines • 6.07 kB
JavaScript
import { BaseMessenger, SDKState } from './shared';
/**
* Host SDK - 在宿主应用中运行,创建iframe并与Guest SDK通信
*/
export class HostSDK extends BaseMessenger {
// ==================== 构造函数 ====================
constructor(config) {
super();
/** 是否已初始化 */
this.initialized = false;
this.config = {
width: '100%',
height: '100%',
timeout: 10000,
src: './',
selector: '#iframe-container',
allowedOrigins: [],
...config,
};
// 设置请求超时时间
this.setTimeout(this.config.timeout || 10000);
}
// ==================== 公共方法 ====================
/**
* 初始化Host SDK
* @param container 容器元素
* @returns Promise<void>
*/
async init() {
if (this.initialized) {
console.warn('HostSDK: Already initialized');
return;
}
this.initialized = true;
this.setState(SDKState.INITIALIZING);
try {
// 创建iframe
this.createIframe();
// 设置消息监听
this.setupMessageListener();
// 等待Guest SDK就绪
await this.waitForReady();
this.setState(SDKState.READY);
console.info('HostSDK: Initialized successfully');
}
catch (error) {
this.setState(SDKState.IDLE);
throw error;
}
}
/**
* 销毁Host SDK
*/
destroy() {
if (this.getState() === SDKState.DESTROYED) {
return;
}
// 清理消息监听器
if (this.messageListener) {
window.removeEventListener('message', this.messageListener);
this.messageListener = undefined;
}
// 移除iframe
if (this.iframe) {
this.iframe.remove();
this.iframe = undefined;
}
// 调用父类的销毁方法
super.destroy();
this.initialized = false;
console.info('HostSDK: Destroyed');
}
/**
* 获取iframe元素
*/
getIframe() {
return this.iframe;
}
/**
* 获取配置
*/
getConfig() {
return { ...this.config };
}
/**
* 更新配置
* @param newConfig 新配置
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
// 如果iframe存在,更新样式
if (this.iframe) {
if (newConfig.width) {
this.iframe.style.width = newConfig.width;
}
if (newConfig.height) {
this.iframe.style.height = newConfig.height;
}
}
}
/**
* 重新加载iframe
*/
reload() {
if (this.iframe) {
const currentSrc = this.iframe.src;
this.iframe.src = '';
this.iframe.src = currentSrc;
}
}
// ==================== 受保护的方法 ====================
/**
* 实现抽象方法:发送消息到iframe
* @param message 要发送的消息
*/
sendMessage(message) {
if (!this.iframe?.contentWindow) {
console.warn('Cannot send message: iframe not ready');
return;
}
if (this.getState() === SDKState.DESTROYED) {
console.warn('Cannot send message: SDK is destroyed');
return;
}
try {
this.iframe.contentWindow.postMessage(message, '*');
}
catch (error) {
console.error('Failed to send message to iframe:', error);
}
}
// ==================== 私有方法 ====================
/**
* 创建iframe元素
*/
createIframe() {
this.iframe = document.createElement('iframe');
// 设置基本属性
this.iframe.src = this.config.src;
this.iframe.style.width = this.config.width || '100%';
this.iframe.style.height = this.config.height || '100%';
this.iframe.style.border = 'none';
this.iframe.style.display = 'block';
// 设置安全属性
this.iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');
// 获取容器元素并添加iframe
const parent = document.querySelector(this.config.selector);
if (!parent) {
throw new Error(`Container not found with selector: ${this.config.selector}`);
}
parent.appendChild(this.iframe);
console.info('HostSDK: Iframe created with src:', this.config.src);
}
/**
* 设置消息监听器
*/
setupMessageListener() {
this.messageListener = (event) => {
// 验证消息来源
if (!this.isValidMessageSource(event)) {
return;
}
// 处理接收到的消息
this.handleIncomingMessage(event.data);
};
window.addEventListener('message', this.messageListener);
}
/**
* 验证消息来源是否有效
* @param event 消息事件
*/
isValidMessageSource(event) {
// 检查是否来自我们的iframe
if (event.source !== this.iframe?.contentWindow) {
return false;
}
// 检查来源域名(如果配置了限制)
if (this.config.allowedOrigins && this.config.allowedOrigins.length > 0) {
return this.config.allowedOrigins.includes(event.origin);
}
return true;
}
/**
* 等待Guest SDK就绪
* @returns Promise<void>
*/
waitForReady() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Guest SDK ready timeout'));
}, this.config.timeout);
this.on('ready', () => {
clearTimeout(timeout);
resolve();
});
});
}
}
//# sourceMappingURL=host.js.map