Связь между вкладками или окнами


Я искал способ, как общаться между несколькими вкладками или окнами в браузере (на том же домене, не CORS), не оставляя следов. Было несколько решений:

  1. использование объекта window
  2. postMessage
  3. печенье
  4. localStorage

Первое, вероятно, худшее решение - вам нужно открыть окно из вашего текущего окна, а затем вы можете общаться только до тех пор, пока вы сохраняете окна открыты. Если вы перезагрузите страницу в любом из окон, вы, скорее всего, потеряли связь.

Второй подход, использующий postMessage, вероятно, позволяет осуществлять межродовую коммуникацию, но страдает от той же проблемы, что и первый подход. Вам нужно поддерживать объект window.

Третий способ, используя файлы cookie, хранить некоторые данные в браузере, что может эффективно выглядеть как отправка сообщения всем окнам в одном домене, но проблема в том, что вы никогда не можете знать, все ли вкладки читаются. "сообщение"уже или не до уборки. Вы должны реализовать какой-то тайм-аут, чтобы периодически читать куки. Кроме того, вы ограничены максимальной длиной печенья, которая составляет 4 КБ.

Четвертое решение, используя localStorage, казалось бы, преодолело ограничения cookies,и его можно даже слушать, используя события. Как им пользоваться описано в принятом ответе.

Edit 2018: принятый ответ все еще работает, но есть более новое решение для современных браузеров, чтобы используйте BroadcastChannel. Смотрите другой ответ для простого примера, описывающего, как легко передавать сообщение между вкладками с помощью BroadcastChannel.

8 95

8 ответов:

Edit 2018: вы можете лучше использовать BroadcastChannel для этой цели, смотрите другие ответы ниже. Тем не менее, если вы все еще предпочитаете использовать localstorage для связи между вкладками, сделайте это следующим образом:

Чтобы получать уведомления, когда вкладка отправляет сообщение на другие вкладки,вам просто нужно привязать событие' storage'. Во всех вкладках выполните следующее:

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

Функция 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.
}

Итак, теперь, когда ваши вкладки привязываются к событию onstorage, и у вас реализованы эти две функции, вы можете просто передать сообщение другим вызовам вкладок, например:

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

Помните, что отправка одного и того же сообщения дважды будет распространяться только один раз, поэтому, если вам нужно повторить сообщения, добавьте к ним какой-нибудь уникальный идентификатор, например

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

Также помните, что текущая вкладка, которая передает сообщение, фактически не получает его, только другие вкладки или окна в том же домене.

Вы можете спросить, что произойдет, если пользователь загрузит другую веб-страницу или закроет свою вкладку сразу после вызова setItem() перед removeItem(). Ну, от моего собственного тестирования браузер ставит выгрузку на удержание до тех пор, пока вся функция message_broadcast() закончено. Я протестировал, чтобы положить туда какой-то очень длинный цикл (), и он все еще ждал завершения цикла перед закрытием. Если пользователь убивает вкладку только между ними, то у браузера не будет достаточно времени, чтобы сохранить сообщение на диск, таким образом, этот подход кажется мне безопасным способом, как отправлять сообщения без каких-либо следов. Комментарии приветствуются.

Существует современный 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.

Для тех, кто ищет решение, не основанное на jQuery, это простая версия JavaScript решения, предоставленного Thomas M:

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);
    }
}

Проверка Акростабы - простая связь между вкладками браузера кросс-origin. он использует комбинацию postMessage и sessionStorage API, чтобы сделать связь намного проще и надежнее.


Существуют различные подходы, и каждый из них имеет свои преимущества и недостатки. Давайте обсудим каждый из них:
  1. Локальный склад

    Плюсы :

    1. веб-хранилище можно просмотреть упрощенно говоря, как улучшение куки, обеспечивая гораздо большую емкость хранилища. Если вы посмотрите на исходный код Mozilla, то увидите, что 5120KB (5 Мб , что равно 2,5 млн символов в Chrome) - это размер хранилища по умолчанию для всего домена. Это дает вам значительно больше пространства для работы, чем типичное печенье 4KB.
    2. данные не отправляются обратно на сервер для каждого HTTP-запроса (HTML, images, JavaScript, CSS и т. д.) - уменьшение объема трафика между клиентом и сервером.
    3. данные, хранящиеся в localStorage, сохраняются до явного удаления. Внесенные изменения сохраняются и доступны для всех текущих и будущих посещений сайта.

    Минусы :

    1. он работает на политике того же происхождения. Таким образом, сохраненные данные будут доступны только на том же источнике.
  2. Печенье

    Плюсы:

    1. по сравнению с другими, нет ничего АФАИК.

    Минусы:

    1. ограничение 4K распространяется на весь файл cookie, включая имя, значение, дату истечения срока действия и т. д. Для поддержки большинства браузеров имя файла должно быть меньше 4000 байт, а общий размер файла cookie-меньше 4093 байт.
    2. Данные отправляются обратно на сервер для каждого HTTP-запроса (HTML, images, JavaScript, CSS и т. д.) - увеличивая объем трафика между клиентом и сервером.

      Обычно допускается следующее:

      • 300 печенье в общей сложности
      • 4096 байт на файл cookie
      • 20 файлов cookie на домен
      • 81920 байт на домен(дано 20 файлов cookie максимального размера 4096 = 81920 байт.)
  3. SessionStorage

    Плюсы:

    1. это похоже на localStorage.
    2. изменения доступны только для одного окна (или вкладки в браузерах, таких как Chrome и Firefox). Внесенные изменения сохраняются и доступны для текущая страница, а также будущие посещения сайта в этом же окне. Как только окно закрывается, хранилище удаляется

    Минусы:

    1. данные доступны только внутри окна / вкладки, в которой они были установлены.
    2. данные не являются постоянными, то есть они будут потеряны после закрытия окна/вкладки.
    3. Как и localStorage, tt работает наполитике того же происхождения . Таким образом, сохраненные данные будут доступны только на том же самом происхождение.
  4. PostMessage

    Плюсы:

    1. безопасно обеспечивает перекрестную связь.
    2. в качестве точки данных реализация WebKit (используемая Safari и Chrome) в настоящее время не устанавливает никаких ограничений (кроме тех, которые накладываются из-за нехватки памяти).

    Минусы:

    1. Нужно открыть окно из текущего окна, а затем можно общаться только до тех пор, пока вы держите окна открыты.
    2. проблемы безопасности - отправка строк через postMessage означает, что вы будете получать другие события postMessage, опубликованные другими плагинами JavaScript, поэтому не забудьте реализовать targetOrigin и проверку на вменяемость данных, передаваемых слушателю сообщений.
  5. Комбинация PostMessage + SessionStorage

    Использование postMessage для связи между несколькими вкладками и одновременно использование sessionStorage во всех вновь открытых вкладках / окнах для сохранения передаваемых данных. Данные будут сохраняться до тех пор, пока вкладки/окна остаются открытыми. Таким образом, даже если открывающая вкладка/окно будет закрыта, открытые вкладки/окна будут иметь все данные даже после обновления.

Для этого я написал библиотеку JavaScript с именем Акростабы который использует postMessage API для связи между вкладками/окнами кросс-происхождения и sessionStorage для сохранения открытые вкладки / окна идентичности, пока они живут.

Еще один метод, который люди должны рассмотреть с помощью общих работников. Я знаю, что это ультрасовременная концепция, но вы можете создать ретранслятор на общем работнике, который намного быстрее, чем 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

P.S. Я взял на себя смелость рекомендовать его здесь, так как большинство компонентов "lock / mutex / sync" отказывают на соединениях websocket, когда события происходят почти одновременно

Я создал модуль, который работает так же, как официальный Broadcastchannel, но имеет резервные копии, основанные на localstorage, indexeddb и unix-сокетах. Это гарантирует, что он всегда работает даже с Webworkers или NodeJS. Смотрите pubkey: BroadcastChannel

Я написал об этом статью в своем блоге: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a-web-application .

Используя библиотеку, которую я создал 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

Есть и другие удобные методы для обработки других сценариев, а также