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

使用window对象 postMessage 饼干 localStorage

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

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

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

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


当前回答

我在我的博客上写过一篇文章:跨浏览器选项卡共享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

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

其他回答

我在我的博客上写过一篇文章:跨浏览器选项卡共享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

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

人们应该考虑使用的另一种方法是共享工作者。我知道这是一个前沿的概念,但你可以在共享工作上创建一个中继,它比本地存储快得多,并且不需要父/子窗口之间的关系,只要你在同一个原点上。

在这里可以看到我关于这个问题的一些讨论。

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

为此,您最好使用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连接上失败。