Javascript 选项卡或窗口之间的通信

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28230845/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:24:57  来源:igfitidea点击:

Communication between tabs or windows

javascripthtmlbrowserbroadcast-channel

提问by Tomas M

I was searching for a way how to communicate between multiple tabs or windows in a browser (on the same domain, not CORS) without leaving traces. There were several solutions:

我正在寻找一种如何在浏览器中的多个选项卡或窗口(在同一域上,而不是 CORS 上)之间进行通信而不留下痕迹的方法。有几种解决方案:

  1. using window object
  2. postMessage
  3. cookies
  4. localStorage
  1. 使用窗口对象
  2. 留言
  3. 饼干
  4. 本地存储

The first is probably the worst solution - you need to open a window from your current window and then you can communicate only as long as you keep the windows open. If you reload the page in any of the windows, you most likely lost the communication.

第一个可能是最糟糕的解决方案 - 您需要从当前窗口打开一个窗口,然后只有在保持窗口打开的情况下才能进行通信。如果您在任何窗口中重新加载页面,您很可能会丢失通信。

Second approach, using postMessage, probably enables cross-origin communication, but suffers the same problem as the first approach. You need to maintain a window object.

第二种方法,使用 postMessage,可能实现跨域通信,但遇到与第一种方法相同的问题。您需要维护一个窗口对象。

Third way, using cookies, store some data in the browser, which can effectively look like sending a message to all windows on the same domain, but the problem is that you can never know if all tabs read the "message" already or not before cleaning up. You have to implement some sort of timeout to read the cookie periodically. Furthermore you are limited by maximum cookie length, which is 4KB.

第三种方式,使用cookies,在浏览器中存储一些数据,这可以有效地看起来像向同一域中的所有窗口发送消息,但问题是你永远无法知道所有标签之前是否已经阅读了“消息”打扫干净。您必须实现某种超时才能定期读取 cookie。此外,您受到最大 cookie 长度的限制,即 4KB。

Fourth solution, using localStorage, seemed to overcome the limitations of cookies, and it can be even listen-to using events. How to use it is described in the accepted answer.

第四个方案,使用localStorage,似乎克服了cookies的限制,甚至可以使用事件监听。已接受的答案中描述了如何使用它。

Edit 2018: the accepted answer still works, but there is a newer solution for modern browsers, to use BroadcastChannel. See the other answer for a simple example describing how to easily transmit message between tabs by using BroadcastChannel.

编辑 2018 年:接受的答案仍然有效,但现代浏览器有一个更新的解决方案,可以使用 BroadcastChannel。有关描述如何使用 BroadcastChannel 在选项卡之间轻松传输消息的简单示例,请参阅其他答案。

采纳答案by Tomas M

Edit 2018: You may better use BroadcastChannel for this purpose, see other answers below. Yet if you still prefer to use localstorage for communication between tabs, do it this way:

2018 年编辑:您最好为此目的使用 BroadcastChannel,请参阅下面的其他答案。但是,如果您仍然喜欢使用 localstorage 在选项卡之间进行通信,请这样做:

In order to get notified when a tab sends a message to other tabs, you simply need to bind on 'storage' event. In all tabs, do this:

为了在选项卡向其他选项卡发送消息时得到通知,您只需绑定“存储”事件。在所有选项卡中,执行以下操作:

$(window).on('storage', message_receive);

The function message_receivewill be called every time you set any value of localStorage in any other tab. The event listener contains also the data newly set to localStorage, so you don't even need to parse localStorage object itself. This is very handy because you can reset the value just right after it was set, to effectively clean up any traces. Here are functions for messaging:

message_receive每次您在任何其他选项卡中设置 localStorage 的任何值时,都会调用该函数。事件侦听器还包含新设置到 localStorage 的数据,因此您甚至不需要解析 localStorage 对象本身。这非常方便,因为您可以在设置后立即重置该值,以有效清除任何痕迹。以下是消息传递函数:

// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
    localStorage.setItem('message',JSON.stringify(message));
    localStorage.removeItem('message');
}


// receive message
//
function message_receive(ev)
{
    if (ev.originalEvent.key!='message') return; // ignore other keys
    var message=JSON.parse(ev.originalEvent.newValue);
    if (!message) return; // ignore empty msg or msg reset

    // here you act on messages.
    // you can send objects like { 'command': 'doit', 'data': 'abcd' }
    if (message.command == 'doit') alert(message.data);

    // etc.
}

So now once your tabs bind on the onstorage event, and you have these two functions implemented, you can simply broadcast a message to other tabs calling, for example:

因此,现在一旦您的选项卡绑定在 onstorage 事件上,并且您实现了这两个功能,您就可以简单地将消息广播给其他选项卡调用,例如:

message_broadcast({'command':'reset'})

Remember that sending the exact same message twice will be propagated only once, so if you need to repeat messages, add some unique identifier to them, like

请记住,两次发送完全相同的消息只会传播一次,因此如果您需要重复消息,请为它们添加一些唯一标识符,例如

message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})

Also remember that the current tab which broadcasts the message doesn't actually receive it, only other tabs or windows on the same domain.

还要记住,广播消息的当前选项卡实际上并没有接收它,只有同一个域中的其他选项卡或窗口。

You may ask what happens if the user loads a different webpage or closes his tab just after the setItem() call before the removeItem(). Well, from my own testing the browser puts unloading on hold until the entire function message_broadcast()is finished. I tested to put inthere some very long for() cycle and it still waited for the cycle to finish before closing. If the user kills the tab just inbetween, then the browser won't have enough time to save the message to disk, thus this approach seems to me like safe way how to send messages without any traces. Comments welcome.

您可能会问,如果用户在 removeItem() 之前的 setItem() 调用之后加载不同的网页或关闭他的选项卡会发生什么。好吧,根据我自己的测试,浏览器会暂停卸载,直到整个功能message_broadcast()完成。我测试在那里放入一些很长的 for() 循环,它仍然在等待循环完成后再关闭。如果用户在中间杀死选项卡,那么浏览器将没有足够的时间将消息保存到磁盘,因此在我看来,这种方法似乎是一种安全的方式来发送消息而没有任何痕迹。欢迎评论。

回答by user

There is a modern API dedicated for this purpose - Broadcast Channel

有一个专门用于此目的的现代 API -广播频道

It is as easy as:

这很简单:

var bc = new BroadcastChannel('test_channel');

bc.postMessage('This is a test message.'); /* send */

bc.onmessage = function (ev) { console.log(ev); } /* receive */

There is no need for the message to be just a DOMString, any kind of object can be sent.

消息不需要只是一个 DOMString,任何类型的对象都可以发送。

Probably, apart from API cleanness, it is the main benefit of this API - no object stringification.

或许,除了 API 整洁之外,这是这个 API 的主要好处——没有对象字符串化。

Currently supportedonly in Chrome and Firefox, but you can find a polyfill that uses localStorage.

目前仅支持Chrome 和 Firefox,但您可以找到使用 localStorage 的 polyfill。

回答by Nacho Coloma

For those searching for a solution not based on jQuery, this is a plain JavaScript version of the solution provided by Thomas M:

对于那些寻找不基于 jQuery 的解决方案的人来说,这是 Thomas M 提供的解决方案的纯 JavaScript 版本:

window.addEventListener("storage", message_receive);

function message_broadcast(message) {
    localStorage.setItem('message',JSON.stringify(message));
}

function message_receive(ev) {
    if (ev.key == 'message') {
        var message=JSON.parse(ev.newValue);
    }
}

回答by softvar

Checkout AcrossTabs- Easy communication between cross-origin browser tabs.It uses a combination of postMessageand sessionStorageAPI to make communication much easier and reliable.

Checkout AcrossTabs-跨域浏览器选项卡之间的轻松通信。它结合使用postMessagesessionStorageAPI 使通信变得更加容易和可靠。



There are different approaches and each one has its own advantages and disadvantages. Lets discuss each:

有不同的方法,每种方法都有自己的优点和缺点。让我们讨论每一个:

  1. LocalStorage

    Pros:

    1. Web storage can be viewed simplistically as an improvement on cookies, providing much greater storage capacity. If you look at the Mozilla source code we can see that 5120KB(5MBwhich equals 2.5 Million charson Chrome) is the default storage size for an entire domain. This gives you considerably more space to work with than a typical 4KB cookie.
    2. The data is not sent back to the server for every HTTP request (HTML, images, JavaScript, CSS, etc) - reducing the amount of traffic between client and server.
    3. The data stored in localStorage persists until explicitly deleted. Changes made are saved and available for all current and future visits to the site.

    Cons:

    1. It works on same-origin policy. So, data stored will only be able available on the same origin.
  2. Cookies

    Pros:

    1. Compared to others, there's nothing AFAIK.

    Cons:

    1. The 4K limit is for the entire cookie, including name, value, expiry date etc. To support most browsers, keep the name under 4000 bytes, and the overall cookie size under 4093 bytes.
    2. The data is sent back to the server for every HTTP request (HTML, images, JavaScript, CSS, etc) - increasing the amount of traffic between client and server.

      Typically, the following are allowed:

      • 300cookies in total
      • 4096 bytesper cookie
      • 20 cookiesper domain
      • 81920 bytesper domain(Given 20 cookies of max size 4096 = 81920 bytes.)
  3. sessionStorage

    Pros:

    1. It is similar to localStorage.
    2. Changes are only available per window (or tab in browsers like Chrome and Firefox). Changes made are saved and available for the current page, as well as future visits to the site on the same window. Once the window is closed, the storage is deleted

    Cons:

    1. The data is available only inside the window/tab in which it was set.
    2. The data is not persistent i.e. it will be lost once the window/tab is closed.
    3. Like localStorage, tt works on same-origin policy. So, data stored will only be able available on the same origin.
  4. PostMessage

    Pros:

    1. Safely enables cross-origincommunication.
    2. As a data point, the WebKit implementation (used by Safari and Chrome) doesn't currently enforce any limits (other than those imposed by running out of memory).

    Cons:

    1. Need to open a window from the current window and then can communicate only as long as you keep the windows open.
    2. Security concerns- Sending strings via postMessage is that you will pick up other postMessage events published by other JavaScript plugins, so be sure to implement a targetOriginand a sanity check for the data being passed on to the messages listener.
  5. A combination of PostMessage+ SessionStorage

    Using postMessage to communicate between multiple tabs and at the same time using sessionStorage in all the newly opened tabs/windows to persist data being passed. Data will be persisted as long as the tabs/windows remain opened. So, even if the opener tab/window gets closed, the opened tabs/windows will have the entire data even after getting refreshed.

  1. 本地存储

    优点

    1. Web 存储可以简单地视为对 cookie 的改进,提供了更大的存储容量。如果您查看 Mozilla 源代码,我们可以看到5120KB5MB,在 Chrome 上相当于250 万个字符)是整个域的默认存储大小。与典型的 4KB cookie 相比,这为您提供了更多的工作空间。
    2. 不会为每个 HTTP 请求(HTML、图像、JavaScript、CSS 等)将数据发送回服务器 - 减少客户端和服务器之间的流量。
    3. 存储在 localStorage 中的数据会一直存在,直到被明确删除。所做的更改将被保存并可供所有当前和将来访问该站点时使用。

    缺点

    1. 它适用于同源策略。因此,存储的数据只能在同一来源上可用。
  2. 饼干

    优点:

    1. 与其他人相比,没有什么是AFAIK。

    缺点:

    1. 4K 限制是针对整个 cookie,包括名称、值、到期日期等。为了支持大多数浏览器,将名称保持在 4000 字节以下,并且整个 cookie 大小在 4093 字节以下。
    2. 对于每个 HTTP 请求(HTML、图像、JavaScript、CSS 等),数据都会发送回服务器 - 增加了客户端和服务器之间的流量。

      通常,允许以下内容:

      • 总共300 个饼干
      • 每个 cookie 4096 字节
      • 每个域20 个 cookie
      • 每个域81920 字节(给定 20 个最大大小为 4096 = 81920 字节的 cookie。)
  3. 会话存储

    优点:

    1. 它类似于localStorage.
    2. 更改仅适用于每个窗口(或 Chrome 和 Firefox 等浏览器中的选项卡)。所做的更改将被保存并可用于当前页面,以及将来在同一窗口中访问该站点。窗口关闭后,存储将被删除

    缺点:

    1. 数据仅在设置它的窗口/选项卡内可用。
    2. 数据不是持久的,即一旦窗口/选项卡关闭,它就会丢失。
    3. 就像localStorage, tt 适用于同源策略。因此,存储的数据只能在同一来源上可用。
  4. 留言

    优点:

    1. 安全地启用跨域通信。
    2. 作为数据点,WebKit 实现(由 Safari 和 Chrome 使用)目前不强制执行任何限制(除了因内存不足而施加的限制)。

    缺点:

    1. 需要从当前窗口打开一个窗口,然后只有保持窗口打开才能进行通信。
    2. 安全问题- 通过 postMessage 发送字符串是您将获取由其他 JavaScript 插件发布的其他 postMessage 事件,因此请确保对targetOrigin传递给消息侦听器的数据进行完整性检查。
  5. PostMessage+ SessionStorage 的组合

    使用 postMessage 在多个选项卡之间进行通信,同时在所有新打开的选项卡/窗口中使用 sessionStorage 来保存正在传递的数据。只要选项卡/窗口保持打开状态,数据就会一直存在。因此,即使打开器选项卡/窗口被关闭,打开的选项卡/窗口即使在刷新后也会拥有整个数据。

I have written a JavaScript library for this, named AcrossTabswhich uses postMessage API to communicate between cross-origin tabs/windows and sessionStorage to persist the opened tabs/windows identity as long as they live.

我为此编写了一个名为AcrossTabs的 JavaScript 库,它使用 postMessage API 在跨域选项卡/窗口和 sessionStorage 之间进行通信,以在打开的选项卡/窗口身份存在时一直保持它们。

回答by datasedai

Another method that people should consider using is Shared Workers. I know it's a cutting edge concept, but you can create a relay on a Shared Worker that is MUCH faster than localstorage, and doesn't require a relationship between the parent/child window, as long as you're on the same origin.

人们应该考虑使用的另一种方法是共享工人。我知道这是一个前沿概念,但是您可以在共享 Worker 上创建一个比 localstorage 快得多的中继,并且不需要父/子窗口之间的关系,只要您在同一来源上。

See my answer herefor some discussion I made about this.

见我的答案在这里的一些讨论我这个做。

回答by Alex

There's a tiny open-source component to sync/communicate between tabs/windows of the same origin (disclaimer - I'm one of the contributors!) based around localStorage.

基于localStorage.

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
    DoSomething();
});

TabUtils.CallOnce("lockname", function () {
    alert("I run only once across multiple tabs");
});

https://github.com/jitbit/TabUtils

https://github.com/jitbit/TabUtils

P.S. I took the liberty to recommend it here since most of the "lock/mutex/sync" components fail on websocket connections when events happen almost simultaneously

PS 我冒昧在这里推荐它,因为当事件几乎同时发生时,大多数“锁定/互斥/同步”组件在 websocket 连接上失败

回答by jcubic

I've created a library sysend.js, it's very small, you can check its source code. The library don't have any external dependencies.

我创建了一个库sysend.js,它非常小,你可以查看它的源代码。该库没有任何外部依赖项。

You can use it for communication between tabs/windows in same browser and domain. The library use BroadcastChannel, if supported, or storage event from localStorage.

您可以使用它在同一浏览器和域中的选项卡/窗口之间进行通信。该库使用 BroadcastChannel(如果支持)或来自 localStorage 的存储事件。

API is very simple:

API非常简单:

sysend.on('foo', function(message) {
    console.log(message);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo'); // empty notification

when your brower support BroadcastChannel it sent literal object (but it's in fact auto-serialized by browser) and if not it's serialized to JSON first and deserialized on other end.

当您的浏览器支持 BroadcastChannel 时,它会发送文字对象(但它实际上是由浏览器自动序列化的),如果不是,则首先将其序列化为 JSON,然后在另一端反序列化。

Recent version also have helper API to create proxy for Cross-Domain communication. (it require single html file on target domain).

最近的版本还有帮助 API 来创建跨域通信的代理。(它需要目标域上的单个 html 文件)。

Here is demo.

这是演示

EDIT:

编辑

New version also support Cross-Domaincommunication, if you include special proxy.htmlfile on target domain and call proxyfunction from source domain:

新版本还支持跨域通信,如果您proxy.html在目标域中包含特殊文件并proxy从源域调用函数:

sysend.proxy('https://target.com');

(proxy.html it's very simple html file, that only have one script tag with the library).

(proxy.html 这是一个非常简单的 html 文件,库中只有一个脚本标签)。

If you want two way communication you need to do the same on target.comdomain.

如果你想要双向通信,你需要在target.com域上做同样的事情。

NOTE: If you will implement same functionality using localStorage, there is issue in IE. Storage event is sent to the same window, which triggered the event and for other browsers it's only invoked for other tabs/windows.

注意:如果您将使用 localStorage 实现相同的功能,则 IE 中存在问题。存储事件被发送到触发该事件的同一窗口,对于其他浏览器,它仅被其他选项卡/窗口调用。

回答by pubkey

I created a module that works equal to the official Broadcastchannelbut has fallbacks based on localstorage, indexeddb and unix-sockets. This makes sure it always works even with Webworkers or NodeJS. See pubkey:BroadcastChannel

我创建了一个模块,它的工作原理与官方Broadcastchannel相同,但具有基于 localstorage、indexeddb 和 unix-sockets 的回退。这确保它即使与 Webworkers 或 NodeJS 也始终有效。见pubkey:BroadcastChannel

回答by adentum

I wrote an article on this on my blog: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a-web-application.

我在我的博客上写了一篇关于此的文章:http: //www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a-网络应用程序

Using a library I created storageManageryou can achieve this as follows:

使用我创建的库,storageManager您可以按如下方式实现:

storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data

There are other convenient methods as well to handle other scenarios as well

还有其他方便的方法来处理其他场景

回答by Kamil Kie?czewski

This is a development storagepart of Tomas M answerfor Chrome. We must add listener

这是ChromestorageTomas M answer的开发部分。我们必须添加监听器

window.addEventListener("storage", (e)=> { console.log(e) } );

Load/save item in storage not runt this event - we MUST trigger it manually by

在存储中加载/保存项目不会运行此事件 - 我们必须手动触发它

window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME

and now, all open tab-s will receive event

现在,所有打开的选项卡都将收到事件