如何在浏览器标签页之间共享单个JS资源?

4
例如,我想使用socket.io、长轮询等技术来在多个选项卡中刷新聊天消息。为此,我希望只使用一个连接处理所有选项卡。我该如何实现?可以将公共数据存储在localStorage、cookie等中,并且需要某种信号量,使得一个选项卡只能获取一个同步资源,在该选项卡关闭后,它将分配给另一个选项卡,以此类推。这是否可行?唯一想到的解决方法是告诉localStorage在onbeforeunload时释放资源,但这在每个浏览器中都不起作用。是否有其他选项?

每个选项卡都是单独的URL吗? - jcollum
如果这不重要,我为什么要问呢? - jcollum
选项卡中的URL不一定不同,但它们可以是不同的。因此,您不能使用URL来识别选项卡。顺便说一句,使用Math.random为每个选项卡生成唯一ID更容易,因此这个问题并没有真正帮助... - inf3rno
2个回答

9
这个问题中的关键词是“跨标签通信”,“跨窗口消息传递”等等...
一种解决方案类似于长轮询:使用本地存储进行跨标签通信/ 定期向localStore/cookies询问更改,并添加队列以分配公共资源(例如socket.io连接)。您可以使用onbeforeunload或timeout来确定选项卡/窗口是否被导航到其他页面或关闭。之后,队列中的下一个选项卡将快速分配资源...
第二个解决方案是使用“localStore存储事件”来实现相同的功能。在这种情况下,如果有onbeforeunload事件,则不必定期询问localStore。根据此:localStorage eventHandler Doesn't Get Called 存储事件仅设计用于影响其他选项卡,因此它们是进行选项卡间通信的不错选择。唯一的问题是onunload:local storage on window unload event 。因此,由于缺乏onunload支持,第一种解决方案可能更好。
第三种解决方案是使用“共享WebWorker”,但它们尚未在多个浏览器(Internet Explorer)中实现,或者它们无法打开套接字(Firefox)。因此,目前它们不是一个选项,也许在修复错误后1-2年后会有所改善...这里有一个演示-仅适用于Chrome-html5-shared-web-worker-examples
第四种解决方案是window.postMessage,但当前并没有完整的浏览器支持。我在一些sto问题中读到了关于它的信息,他们都写道postMessage不能胜任我们想要的任务。我没有检查该函数的确切细节,我认为这不值得时间...有一个关于跨域交互iframe通信的示例:Cross-Domain iframe communication,但我认为它不能用于同域窗口之间的通信。
第五种解决方案是使用cookie,但在这种情况下,每个标签页都应该ping document.cookie,因为没有像localstore中的storage事件一样的cookie更改事件。 BNC Connector 使用此方法。
第六种解决方案是使用WebSQL。它的驱动程序是异步非阻塞的,所以比localStorage更好,但目前它不受Firefox和MSIE支持。
结论:
现在-2013.10.03-定期从资源利用器标签中ping localStore是最好的选择。其他标签应该监听时间戳更新的存储事件。如果该事件未及时到达,则资源利用器标签已超时,队列中的下一个标签应获取资源。也许稍后的onunload事件或共享工作者将是可靠的,但目前它们还不够好...
解决方案:
我找到了一种实现结论中描述的方法的方法:intercom.js">intercom.js。我添加了一个关于资源共享通用接口的issue,但在我的情况下,单个socket.io资源已经足够好了...

localStorage支持 vs postMessage支持。除了Opera Mini之外,localStorage在所有设备上都得到支持,而同源的postMessage在IE 8-9之外的所有设备上都得到支持,因此取决于您的目标受众,哪个更可取。 - Tgr
哈哈,目标是Opera Mini的吗? :D - inf3rno
2
根据StatCounter的数据,Opera在移动端市场占有约15%的份额(IE8 / 9在桌面端的份额也大致相同)。这不是我在开发移动中心应用程序时会轻易忽略的数字。 - Tgr

0
在我看来,最好的方法是使用共享工作器来调节标签之间的资源,并使用广播通道从共享工作器向所有浏览器标签进行通信。
问题描述:您有一个要在浏览器标签之间共享的单个资源。在我的情况下,这是一个传入音频连接。问题的本质是该资源必须归属于某个特定的浏览器标签。根本问题是,如果浏览器标签关闭(beforeunload和unload事件无法用于此),则不会给出可靠的通知。需要发生的是,如果拥有资源的浏览器标签关闭,另一个浏览器标签需要接管所有权。
我的解决方案是借鉴SCADA系统实现中的故障转移方法。为了使SCADA系统可靠,它必须防止主计算机系统的故障。为此,备份计算机系统会持续发送hello消息给主计算机。如果主计算机未能响应hello消息,备份计算机将被指示“成为主计算机”。然后,故障转移系统将所有I/O通道从旧的主计算机切换到新的主计算机系统。然后重新启动旧的主计算机。

我已经为浏览器标签实现了相同的方法。共享工作线程首先选择浏览器标签中的“主要”标签。一个问候消息会持续广播到所有浏览器标签,并且每个标签都会回复它们是主要还是备份。如果主要标签没有回应,那么共享工作线程会选择一个备份标签,并指示它成为主要标签。

代码的关键部分是心跳函数,它每5秒钟(默认值)执行以下操作:(1)移除无响应的页面;(2)如有必要选择新的主要标签;(3)对所有浏览器标签进行ping操作。

this.heartbeat = function() {
    self.purgeDeadPages();
    self.selectPrime();
    self.ping();
}

selectPrime函数计算报告自身为素数的页面数量:如果为0,则必须选择一个新的素数;如果>1,则是一种退化条件,除了一个素数之外,其它都必须“备份”。

this.selectPrime = function() {
    let primes = this.getPrimes();
    if(primes.length == 0) {
        let backups = this.getBackups();
        if(backups.length > 0) {
            let newPrime = backups.length == 1 ? backups[0] : backups[Math.floor(Math.random() * backups.length)];
            this.goPrime(newPrime);
            self.pages[newPrime].setStatus(iAmPrimeMessage);
        }
    } else if(primes.length > 1) {
        let newPrime = primes[Math.floor(Math.random() * primes.length)];
        primes.forEach((prime) => {
            if(prime != newPrime) {
                this.goBackup(prime);
                self.pages[prime].setStatus(iAmBackupMessage);
            }
        });
    }
}

所有的代码和测试框架都在https://github.com/RundleAutomation/Failover上。

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接