2025-08-16
JavaScript
00
请注意,本文编写于 120 天前,最后修改于 119 天前,其中某些信息可能已经过时。

目录

js部分
PubSub.js
singletonWrap.js
父页面
iframe子页面

在iframe嵌套的业务中,如果父页面需要向子页面发送消息,通过传统的iframe.contentWindow.postMessagewindow.addEventListener('message',(data) => {})的方式,有时候会出现父页面发送了消息,但是子页面没有收到消息的情况,这一般是由于子页面加载并创建message监听的时机出现了延误。在这里我们通过发布订阅的方案来解决它。

js部分

PubSub.js

发布订阅的clsss实现

js
export class PubSub{ constructor(){ this.subscribers = {}; // 存储订阅者 } // 订阅 subscribe(event,callback){ if(!this.subscribers[event]){ this.subscribers[event] = [] } this.subscribers[event].push(callback) return () => { this.unsubscribe(event,callback) } } unsubscribe(event, callback) { if (!this.subscribers[event]) return; const index = this.subscribers[event].indexOf(callback); if (index > -1) { this.subscribers[event].splice(index, 1); } } // 发布消息 publish(event, ...args) { if (!this.subscribers[event]) return; this.subscribers[event].forEach(fn => { fn.apply(this, args); }); } } export class AdvancedPubSub extends PubSub{ constructor(){ super(); this.cache = {} // 缓存已发布的消息 } // 重写发布方法,支持缓存 publish(event, ...args) { // 缓存消息 if (!this.cache[event]) { this.cache[event] = []; } this.cache[event].push(args); // 正常通知订阅者 super.publish(event, ...args); } // 重写订阅方法,支持先发布再订阅 subscribe(event, callback) { // 如果有缓存的消息,立即执行 if (this.cache[event]) { this.cache[event].forEach(args => { callback.apply(this, args); }); } // 正常订阅 return super.subscribe(event, callback); } }

singletonWrap.js

通过es6 Proxy的方式将一个class包装成单例

js
export function singletonWrap (className) { let ins const proxy = new Proxy(className, { construct (target, args) { if (!ins) { ins = new className(...args) } else { console.warn(`${className}只有一个实例`); } return ins } }) // 修改原型链,防止通过构造函数创建新的实例 className.prototype.constructor = proxy return proxy }

父页面

html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./index.js" type="module"></script> <!-- index.js内容 import {singletonWrap} from "./singletonWrap.js" import {AdvancedPubSub} from "./eventBus.js" const ps = singletonWrap(AdvancedPubSub) window.PUBSUB = new ps() --> <style> #iframe-container { width: 100%; margin-top: 20px; } </style> </head> <body> <div> <button onclick="send()"> 发送 </button> </div> <div id="iframe-container"> <!-- <iframe src="./child.html" width="600" height="400" id="iframe"></iframe> --> </div> <script> function send() { const date = new Date().toLocaleString() console.log("当前时间: ", date) console.log("PUBSUB: ", this.PUBSUB) this.PUBSUB.publish("test01",{ data: date }) setTimeout(() => { let iframe = document.getElementById("iframe") if (!iframe) { const iframeContainer = document.getElementById('iframe-container'); iframe = document.createElement('iframe'); iframe.src = './child.html'; iframe.width = '500'; iframe.height = '400'; iframe.id = "iframe" iframeContainer.appendChild(iframe); } }, 3000); } </script> </body> </html>

iframe子页面

html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> *{ padding: 0; margin: 0; } html,body{ width: 100%; height: 100%; } #container{ height: 100%; width: 100%; background-color: pink; } </style> </head> <body> <div id="container"> 我是child iframe </div> <script> window.onload = () => { console.log("child iframe onload事件触发") this.PUBSUB = window.parent.PUBSUB this.PUBSUB.subscribe("test01",(e) => { const container = document.getElementById("container") container.innerText += `\n ${e.data}` }) } </script> </body> </html>

AdvancedPubSub可以实现先发布再订阅的功能,这样我们在子页面就不会造成由于加载延迟造成之前的消息无法消费的问题

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:繁星

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!