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

使用window对象 postMessage 饼干 localStorage

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

第二种方法(使用postMessage)可能支持跨源通信,但它遇到了与第一种方法相同的问题。您需要维护一个窗口对象。

第三种方法是使用cookie,在浏览器中存储一些数据,这实际上看起来像是向同一域上的所有窗口发送消息,但问题是,在清理之前,您永远无法知道所有选项卡是否已经阅读了“消息”。您必须实现某种类型的超时来定期读取cookie。此外,您还受到最大cookie长度(4 KB)的限制。

第四种解决方案,使用localStorage,似乎克服了cookie的限制,甚至可以使用事件监听。如何使用它在公认的答案中有描述。


当前回答

Checkout acrosstab -跨源浏览器选项卡之间的轻松通信。它使用postMessage和sessionStorage api的组合,使通信更加容易和可靠。


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

LocalStorage Pros: 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 5120 KB (5 MB which equals 2.5 million characters on Chrome) is the default storage size for an entire domain. This gives you considerably more space to work with than a typical 4 KB cookie. 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. 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: It works on same-origin policy. So, data stored will only be able available on the same origin. Cookies Pros: Compared to others, there's nothing AFAIK. Cons: The 4 KB 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. 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: 300 cookies in total 4096 bytes per cookie 20 cookies per domain 81920 bytes per domain (given 20 cookies of the maximum size 4096 = 81920 bytes.) sessionStorage Pros: It is similar to localStorage. 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: The data is available only inside the window/tab in which it was set. The data is not persistent, i.e., it will be lost once the window/tab is closed. Like localStorage, tt works on same-origin policy. So, data stored will only be able available on the same origin. PostMessage Pros: Safely enables cross-origin communication. 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: Need to open a window from the current window and then can communicate only as long as you keep the windows open. 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 targetOrigin and a sanity check for the data being passed on to the messages listener. 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.

我为此写了一个JavaScript库,名为acrosstab,它使用postMessage API在跨源选项卡/窗口和sessionStorage之间进行通信,以持久化打开的选项卡/窗口标识,只要它们存在。

其他回答

为此,您最好使用BroadcastChannel。请看下面的其他答案。然而,如果你仍然喜欢使用localstorage在选项卡之间进行通信,可以这样做:

为了在一个选项卡向其他选项卡发送消息时得到通知,你只需要绑定一个'storage'事件。在所有选项卡中,这样做:

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

每次在任何其他选项卡中设置localStorage的任何值时,都会调用message_receive函数。事件监听器还包含新设置为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.
}

所以现在一旦你的选项卡绑定了onstorage事件,你实现了这两个函数,你可以简单地广播一个消息给其他选项卡调用,例如:

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

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

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

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

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 some very long for() cycle in there and it still waited for the cycle to finish before closing. If the user kills the tab just in-between, 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.

有一个基于localStorage的小开源组件,可以在相同来源的选项卡/窗口之间同步和通信(免责声明-我是贡献者之一!)

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

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

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

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

对于那些寻找不基于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);
    }
}

我在我的博客上写过一篇文章:跨浏览器选项卡共享sessionStorage数据。

使用一个库,我创建了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

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

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

简单如下:

var bc = new BroadcastChannel('test_channel');

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

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

消息没有必要只是一个DOMString。任何类型的对象都可以被发送。

也许,除了API的干净性之外,这个API的主要好处是没有对象字符串化。

它目前只在Chrome和Firefox中被支持,但是你可以找到一个使用localStorage的polyfill。