如何在 Safari 10+ 中使用 BroadcastChannel API 或类似的东西?

Roo*_*bot 12 javascript safari local-storage broadcast-channel

问题:

我需要一个客户端 Javascript 解决方案(jQuery 很好),其中一个浏览器窗口/选项卡中的事件可以广播到同一域上的其他窗口/选项卡。例如,如果购物车在选项卡 A 中更新,选项卡 B 和 C 会收到通知,因此我们可以更新页面上的一些信息,或通知用户页面已过时,或类似的事情。

我试过的:

广播信API符合我的需要,但在IE 11或Safari不起作用。

所以我尝试了这个 polyfill,这样我就可以在任何地方使用 BroadcastChannel API。它在 IE 中有效,但在 Safari 中无效(我相信 BroadcastChannel 仍未定义)。

然后我尝试了sysend.js,它使用 BroadCastChannel 如果它可用,否则使用localStorage. 他们的演示页面在 Safari 中运行良好,但在我的网站上,我发现它在 Safari 9 中运行,但在 10-12 中不起作用(使用 BrowserStack 和一台运行 Safari 12 的真实 Mac 进行测试)。调试他们的脚本,似乎在不同选项卡中更改时应该触发的存储事件localStorage根本不会触发。但这实际上只是当您document.domain设置时才出现问题,我就是这样做的。

我相信这与这个旧的 Chrome 错误相同。但是 Chrome 在 2012-2017 年就有这个问题,而 Safari 显然是在 2017 年左右引入的?

我还没有发现其他人在 Safari 中讨论这个错误,但我可以很容易地证明这一点。打开两个使用相同document.domain值的选项卡并运行这些脚本:

选项卡 A:

$(window).on("storage", function (e) {
    alert('storage was modified');
});
Run Code Online (Sandbox Code Playgroud)

选项卡 B:

localStorage.setItem("test", "123");
Run Code Online (Sandbox Code Playgroud)

在 Safari 9 中,Tab A 将弹出警报。在 Safari 10+ 中,它不会。

如果我删除document.domain,它会起作用。请注意,我们document.domain在这些页面上使用,但在这种情况下,它们实际上位于同一域中。但是document.domain,整个站点的其他场景都需要它,因此我无法将其删除。

我也试过查看store.js。它有一个事件系统,但它似乎只能在同一个选项卡中工作(在任何浏览器中)。除非我遗漏了什么。

那么,当你有一套时,是否有任何 BroadcastChannel polyfill 或类似的库可以在 Safari 10+ 中实际工作document.domain 或者任何其他方式来做到这一点?

笔记:

  • 我知道,广播信和“存储”事件localStorage只火了标签其他比当前。这不是我的问题,实际上对我来说是可取的。
  • 我还看到一些帖子让我相信依赖的替代解决方案localStorage可能无法在 Safari 的隐私浏览模式下工作。编辑:看来这已在 Safari 11修复,因此它确实有效,但在我的测试中,它没有与任何其他选项卡共享 localStorage,甚至是同一窗口中的其他私有选项卡。所以这没有多大帮助。 理想情况下,一个解决方案也可以解决这个问题,但此时我会对在 Safari 10+ 中对我有用的任何东西感到满意。我确实在store.js项目中看到了一个例子,他们说他们制作了它,所以在这种情况下它会回退到cookie,所以至少听起来是可能的。
  • 我试图考虑其他方法来做到这一点,每隔几秒钟setInterval检查localStorage一次更新。即使在理论上,这看起来真的很笨拙且不可靠(您如何知道所有页面何时“收到”更新以便您可以清除它?)。你怎么知道什么时候用这种骇人听闻的方式而不是使用的首选方法之一localStorage?Safari 10+ 会报告它支持,localStorage所以你不能真正检测到它,对吧?它“支持”它,只是不能正常工作。

Roo*_*bot 13

我找到了一个解决方法,但我会保留这个问题,因为我仍然希望看到更好的答案。

作为最后的手段,我最终在两种不同的消息传递方式之间切换,具体取决于浏览器。

基本上,如果是 Safari,我使用https://github.com/pubkey/broadcast-channel(您可以从https://github.com/pubkey/broadcast-channel/blob/master/dist/获得缩小的 vanilla JS 版本lib/browser.min.js)。即使您有一套,这似乎也适用于所有版本document.domain。我认为它在这种情况下使用 indexDB,这似乎完全矫枉过正,但我​​似乎别无选择。

它也适用于较新版本的 Safari 中的 Safari 私人窗口。我已经尝试在我的所有脚本中在私有模式下为旧版本的 Safari 捕获到位,否则它会抛出错误。

如果不是 Safari,我会使用sysend.js,它BroadcastChannel默认使用,并在localStorageIE 11 等情况下使用。

  • 请注意,https://github.com/pubkey/broadcast-channel 仅当要通信的窗口位于 Safari 中的顶层时才有效,但如果一个窗口是 iframe,则无效。由于 Safari 不支持本机 BroadcastChannel,因此它会回退到 localStorage。当在 iframe 中使用 localStorage 时,它​​不仅绑定到当前域的上下文,而且“双重键控”到该域 + window.top 域。 (2认同)
  • https://github.com/pubkey/broadcast-channel 在需要时恢复到 localStorage,因此无需使用额外的库 (2认同)
  • @Roobot我有一个项目,我需要做类似的事情。在大多数情况下,“广播通道”库能够在本机广播通道支持不可用的情况下找到后备(localStorage 或 IndexedDB)。如果您在项目中需要更多控制(就像我所做的那样),第二个参数采用一个可以在其中指定类型的对象。需要明确的是,github.com/pubkey/broadcast-channe 将尽可能使用本机 BroadcastChannel。可能会简化您的代码。请参阅:https://github.com/pubkey/broadcast-channel#set-options-when-creating-a-channel-optional (2认同)
  • @NicholasHaley 哦,有趣!我想我从来没有见过文档的那一部分。我看到它说“这与 BroadcastChannel API 的行为类似”,并假设它实际上根本没有使用本机 API。我没有时间玩它,但这可能是我原来问题的真正答案。 (2认同)