Зв'язок між вкладками чи вікнами


176

Я шукав спосіб спілкування між декількома вкладками чи вікнами у браузері (на одному домені, а не CORS), не залишаючи слідів. Рішення було декілька:

  1. використовуючи об’єкт вікна
  2. постМесаж
  3. печиво
  4. місцеві магазини

Перше, мабуть, найгірше рішення - вам потрібно відкрити вікно з вашого поточного вікна, і тоді ви зможете спілкуватися лише до тих пір, поки ви не будете тримати вікна відкритими. Якщо ви перезавантажите сторінку в будь-якому з вікон, ви, швидше за все, втратили зв’язок.

Другий підхід, використовуючи postMessage, ймовірно, забезпечує взаємозв'язок між походженнями, але має таку ж проблему, що і перший підхід. Вам потрібно підтримувати об’єкт вікна.

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

Четверте рішення, використовуючи localStorage, здавалося, подолало обмеження файлів cookie, і його можна навіть слухати, використовуючи події. Як його використовувати описано у прийнятій відповіді.

Редагувати 2018: прийнята відповідь як і раніше працює, але є сучасне рішення для сучасних браузерів - використовувати BroadcastChannel. Інший відповідь див. Для простого прикладу, що описує, як легко передавати повідомлення між вкладками за допомогою BroadcastChannel.


4
Чому це питання було закрито як "занадто широке", коли майже однакові питання були відкриті роками? Надсилання повідомлення на всі відкриті вікна / вкладки за допомогою JavaScript , stackoverflow.com/questions/2236828/… , як ви спілкуєтесь між двома вкладками / вікнами браузера? і ще кілька.
Дан Даскалеску

Я створив бібліотеку через localStorage та sessionStorage для управління сховищем даних на стороні клієнта. Ви можете робити такі речі, як storageManager.savePermanentData ("дані", "ключ"); або storageManager.saveSyncedSessionData ('дані', 'ключ'); на основі того, як ви хочете, щоб ваші дані поводилися. Це дійсно спрощує процес. Повна стаття тут: ebenmonney.com/blog/…
adentum


2
Я створив бібліотеку sysend.js кілька років тому, в останній версії в ній використовується BroadcastChannel. Ви можете протестувати бібліотеку, відкривши цю сторінку двічі jcubic.pl/sysend.php , вона також працює з різним походженням, якщо ви надасте проксі iframe.
jcubic

Чи розглядаю піддомен як однакове походження? Я маю на увазі, у мене нижче трьох доменів, вони спілкуються через api-канал? alpha.firstdomain.com, beta.firstdomain.com, gama.firstdomain.com
Tejas Patel

Відповіді:


142

Редагувати 2018 рік: Ви можете краще використовувати BroadcastChannel для цієї мети, дивіться інші відповіді нижче. Але якщо ви все ще вважаєте за краще використовувати локальне зберігання для зв'язку між вкладками, зробіть це так:

Щоб отримувати сповіщення, коли вкладка надсилає повідомлення на інші вкладки, вам просто потрібно прив’язати до події "зберігання". На всіх вкладках виконайте це:

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


1
чи можете ви проігнорувати подію видалення перед тим, як викликати JSON.parse ()?
dandavis

1
пам’ятайте про обмеження даних про події, включаючи попередньо наявні дані localStorage. може бути краще / безпечніше використовувати події зберігання лише для обміну повідомленнями, а не доставку. наприклад, коли ви отримуєте листівку, яка повідомляє вам забрати пакет у поштовому відділенні ... також локальне зберігання переходить на жорсткий диск, тому воно може залишати ненавмисні кеші та впливати на журнали, що є ще однією причиною розглянути інший механізм транспортування фактичні дані.
dandavis

1
Я зробив щось, пов’язане з тим часом: danml.com/js/localstorageevents.js , що у вас є база емітерів подій та "локальне відлуння", щоб ви могли використовувати EE для всього, що скрізь.
dandavis

7
Safari не підтримує BroadcastChannel - caniuse.com/#feat=broadcastchannel
Шрікант

1
Ви можете також використовувати спільних робітників: developer.mozilla.org/en-US/docs/Web/API/SharedWorker (який має кращу підтримку у веб-переглядачах)
Seblor

116

Для цього існує сучасний API, призначений для цього - Broadcast Channel

Це так просто, як:

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.


3
Зачекайте, як ви знаєте, звідки походить повідомлення? Чи ігнорує повідомлення, що надходять з тієї ж вкладки?
AturSams

2
@zehelvion: Відправник не отримає його відповідно до, наприклад, цього хорошого огляду . Крім того, ви можете помістити в повідомлення все, що завгодно, в т.ч. якийсь ідентифікатор відправника, якщо потрібно.
Sz.

7
Є чудовий проект, який завершує цю функцію в бібліотеці крос-браузерів тут: github.com/pubkey/broadcast-channel
james2doyle

Чи надходили якісь загальнодоступні сигнали від Safari про те, чи буде підтримка цього API розміщена в цьому веб-переглядачі?
Кейсі

@AturSams Ви перевіряєте, що MessageEvent.origin, MessageEvent.source або MessageEvent.ports - те, що вам потрібно. Як завжди, документація найкраще почати: developer.mozilla.org/en-US/docs/Web/API/MessageEvent
Стефан Михай Михайло Станеску

40

Для тих, хто шукає рішення, яке не базується на 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);
    }
}

1
Чому ви пропустили виклик RemoveItem?
Томаш М

2
Я просто зосереджувався на відмінностях jQuery від JavaScript.
Nacho Coloma

Я завжди використовую lib через polyfill та можливість не підтримується функції!
Амін Рахімі

20

Checkout AcrossTabs - Легкий зв’язок між вкладками браузера між походженнями. Він використовує комбінацію API PostMessage і sessionStorage, щоб зробити спілкування набагато простішим і надійнішим.


Існують різні підходи, і кожен має свої переваги та недоліки. Давайте обговоримо кожен:

  1. LocalStorage

    Плюси :

    1. Веб-сховище можна розглядати спрощено як поліпшення файлів cookie, забезпечуючи значно більшу ємність пам’яті. Якщо ви подивитесь на вихідний код Mozilla, ми можемо побачити, що 5120 КБ ( 5 МБ, що становить 2,5 мільйона символів у Chrome) - це розмір пам’яті за замовчуванням для всього домену. Це дає вам значно більше місця для роботи, ніж типовий файл cookie в 4 КБ.
    2. Дані не надсилаються назад на сервер для кожного HTTP запиту (HTML, зображення, JavaScript, CSS тощо) - зменшуючи кількість трафіку між клієнтом та сервером.
    3. Дані, що зберігаються в localStorage, зберігаються до явного видалення. Внесені зміни зберігаються та доступні для всіх поточних та майбутніх відвідувань сайту.

    Мінуси :

    1. Це працює на політиці одного походження . Таким чином, дані, які зберігаються, зможуть бути доступні лише з тим самим джерелом.
  2. Печиво

    Плюси:

    1. У порівнянні з іншими, AFAIK нічого не має.

    Мінуси:

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

      Зазвичай дозволяється:

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

    Плюси:

    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 під назвою AcrossTabs, яка використовує API postMessage для зв'язку між вкладками / вікнами cross-origin та sessionStorage для збереження відкритої вкладки / ідентичності Windows до тих пір, поки вони живуть.


Використовуючи AcrossTabs, чи можна відкрити інший веб-сайт на іншій вкладці та отримати дані з нього на батьківську вкладку? У мене будуть дані про автентифікацію для іншого веб-сайту.
Мадхур Бхайя

1
Так, ви можете @MadhurBhaiya
софтвар

Найбільша перевага файлів cookie - це те, що він дозволяє домен із перехресним походженням, який зазвичай корисний, коли у вас є набір джерел, таких як "a.target.com", "b.target.com" тощо.
StarPinkER

7

Інший метод, який люди повинні розглянути, - це спільні працівники. Я знаю, що це передова концепція, але ви можете створити ретрансляцію на Спільному працівнику, який набагато швидший, ніж локальний сховище, і не потребує взаємозв'язку між вікном батько / дитина, якщо ви однакові.

Дивіться мою відповідь тут для деякої дискусії, яку я зробив з цього приводу.


7

Існує крихітний компонент з відкритим кодом для синхронізації / спілкування між вкладками / вікнами одного походження (відмова від відповідальності - я один із учасників!) 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

PS Я взяв на себе сміття порекомендувати його тут, оскільки більшість компонентів "lock / mutex / sync" не спрацьовують на з'єднаннях websocket, коли події відбуваються майже одночасно


6

Я створив бібліотеку sysend.js , вона дуже мала, ви можете перевірити її вихідний код. Бібліотека не має зовнішніх залежностей.

Ви можете використовувати його для зв'язку між вкладками / вікнами в тому ж браузері та домені. У бібліотеці використовується BroadcastChannel, якщо він підтримується, або зберігання події від localStorage.

API дуже простий:

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

коли ваш браузер підтримує BroadcastChannel, він надіслав буквальний об’єкт (але насправді він автоматично серіалізується браузером), а якщо ні, то він серіалізується в JSON спочатку та десеріалізується на іншому кінці.

В останній версії також є допоміжний API для створення проксі для міждоменного спілкування. (для цього потрібен один html-файл на цільовому домені).

Ось демонстрація .

Редагувати :

Нова версія також підтримує міждоменне спілкування, якщо ви включите спеціальний proxy.htmlфайл на цільовий домен та proxyфункцію виклику з вихідного домену:

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

(proxy.html це дуже простий html-файл, у якому є лише один тег сценарію з бібліотекою).

Якщо ви хочете двостороннє спілкування, вам потрібно зробити те ж саме в target.comдомені.

ПРИМІТКА . Якщо ви будете реалізовувати таку ж функціональність за допомогою localStorage, в IE є проблема. Подія зберігання надсилається до того самого вікна, яке ініціювало подію, а для інших браузерів вона викликається лише для інших вкладок / вікон.


2
Просто хотів дати тобі кілька кудо. Приємне солодке доповнення, яке просте і дозволяє мені спілкуватися між своїми вкладками, щоб утримати програмне забезпечення попередження про вихід від ударів людей. Хороша робота. Я настійно рекомендую це, якщо ви хочете просте у використанні рішення для обміну повідомленнями.
BrownPony

4

Я створив модуль, який відповідає рівню офіційного Broadcastchannel, але має резервні запаси на основі локального зберігання, indexeddb та unix-сокетів. Це гарантує, що він завжди працює навіть із веб-роботодавцями або NodeJS. Дивіться віскі: BroadcastChannel


1

Я написав статтю про це у своєму блозі: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a- веб-додаток .

Використовуючи створену мною бібліотеку, 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

Є й інші зручні методи, а також для обробки інших сценаріїв


0

Це storageчастина розробки Tomas M відповідь для Chrome. Треба додати слухача

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

Завантаження / збереження предмета у сховищі не поширює цю подію - ми ОБОВ'ЯЗКОВО запустимо її вручну

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

і тепер усі відкриті вкладки отримають подію

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.