Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «SharedWorker у JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`SharedWorker` це потік JavaScript, яким одночасно користуються кілька контекстів одного origin: вкладки, вікна, iframe.** На відміну від звичайного `Worker`, який зникає разом зі своєю вкладкою, SharedWorker живе, доки підключений хоча б один клієнт. Зв'язок іде не напряму, а через `MessagePort`: сторінка бере `shared.port`, викликає `port.start()` і спілкується через `port.postMessage()` / `port.onmessage`, а у воркері кожне нове підключення приходить у подію `onconnect` з власним портом. Завдяки цьому один воркер може тримати спільний WebSocket, спільний кеш чи стан авторизації для всіх вкладок і розсилати їм оновлення. ```javascript const shared = new SharedWorker('shared-worker.js'); shared.port.start(); shared.port.postMessage('Hello from a tab!'); ``` **Ключове:** один потік на всі вкладки одного origin, спілкування через порти, життя доки живий хоч один клієнт, доступу до DOM немає.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`SharedWorker` це потік JavaScript, який можуть використовувати кілька контекстів (вкладок, вікон, iframe) одночасно, якщо вони завантажені з одного `origin` (протокол плюс хост плюс порт).** На відміну від звичайного `Worker`, який «помирає» разом із закритою вкладкою, SharedWorker живе, доки підключене хоча б одне вікно. ## Теорія ### TL;DR - Один потік обслуговує всі вкладки та iframe одного origin. - Створюється через `new SharedWorker('shared-worker.js')`, канал це `shared.port`. - Кожне нове підключення приходить у воркер подією `onconnect` з власним `MessagePort`. - Порт треба активувати: `port.start()`, якщо ви використовуєте `addEventListener` замість `onmessage`. - Живе, доки підключений хоча б один клієнт, потім браузер його зупиняє. - Доступу до DOM немає, дані передаються копією через structured clone. ### Швидкий приклад Код у `shared-worker.js`: ```javascript // shared-worker.js onconnect = (event) => { const port = event.ports[0]; console.log('New connection'); port.postMessage('Hello, tab!'); port.onmessage = (e) => { console.log('Message from a tab:', e.data); // надсилаємо відповідь назад port.postMessage(`Received: ${e.data}`); }; }; ``` Код на кожній вкладці (`main.js`): ```javascript // main.js const shared = new SharedWorker('shared-worker.js'); const port = shared.port; // активуємо канал port.start(); // слухаємо відповіді від воркера port.onmessage = (e) => { console.log('SharedWorker reply:', e.data); }; // надсилаємо повідомлення воркеру port.postMessage('Hello from a tab!'); ``` Що станеться: - усі вкладки під'єднаються до одного й того самого воркера; - кожна з них отримуватиме повідомлення через свій `port`; - воркер може координувати їх, наприклад розсилати спільні дані всім. ### Чим SharedWorker відрізняється від звичайного Worker | Особливість | `Worker` (Dedicated) | `SharedWorker` | | --- | --- | --- | | Використовується | Однією сторінкою | Кількома вкладками та вікнами | | Створюється | `new Worker('worker.js')` | `new SharedWorker('worker.js')` | | Канал зв'язку | `postMessage` напряму | Через об'єкт `port` | | Час життя | Доки відкрита вкладка | Доки підключений хоча б один контекст | | Доступний з інших вкладок | Ні | Так, якщо той самий origin | Схема взаємодії: ```text [ Tab 1 ] [ Tab 2 ] [ Iframe ] | | | +--------> [ SharedWorker Thread ] <+ ^ | | (ports[]) v business logic ``` Усі вкладки під'єднуються до одного потоку воркера, а воркер обмінюється повідомленнями з кожною вкладкою через її `port`. ### Як працює onconnect і MessagePort Коли нова вкладка викликає `new SharedWorker()`, браузер не створює новий потік, якщо воркер уже існує. Він просто викликає подію `onconnect` і передає туди об'єкт `MessagePort`, який і є каналом зв'язку. Отже, один SharedWorker може керувати десятками портів, тобто десятками вкладок. #### Розсилка всім вкладкам ```javascript // shared-worker.js const ports = []; onconnect = (e) => { const port = e.ports[0]; ports.push(port); port.start(); port.onmessage = (event) => { // розсилаємо повідомлення всім підключеним вкладкам for (const p of ports) { p.postMessage(`From a tab: ${event.data}`); } }; }; ``` Тепер, якщо відкрити дві вкладки: - вкладка 1 надішле `port.postMessage("Hi")`, - обидві вкладки отримають `"From a tab: Hi"`. Це вже справжній чат між вкладками. ### Де це корисно | Сценарій | Чому SharedWorker корисний | | --- | --- | | **Спільний чат між вкладками** | можна синхронізувати повідомлення | | **Фонова обробка даних** | одна вкладка рахує, результат отримують усі | | **Спільна підписка на WebSocket** | одне підключення на всі вкладки, економія ресурсів | | **Спільна логіка кешу та IndexedDB** | один фон для всіх | | **Синхронізація стану** | наприклад, спільний токен авторизації між вкладками | #### Що важливо пам'ятати - SharedWorker працює тільки в межах одного origin (`https://example.com` не дорівнює `http://example.com`). - Це не Service Worker, типи воркерів різні і взаємозамінними не є. - Підтримка неповна: у деяких старих браузерах і в Safari на iOS його немає. - Спілкування йде через `port.postMessage()` і `port.onmessage`. #### Безпека та передача даних - Воркери ізольовані: немає доступу до DOM, `window`, `document`. - Дані передаються копією (structured clone). - Для швидкого обміну великими бінарними даними можна використовувати Transferable об'єкти (`ArrayBuffer`). Проста аналогія: > Уявіть, що SharedWorker це офісний сервер у компанії. Кожен працівник (вкладка) може до нього під'єднатися. Сервер зберігає спільні дані і розсилає оновлення всім. Доки працює хоча б один працівник, сервер живий. Коли всі закрили вкладки, сервер автоматично вимикається. Підсумок: | Особливість | Опис | | --- | --- | | **Що робить** | Дає кільком сторінкам одного сайту спільний потік JS | | **Головна перевага** | Спільні логіка і дані між вкладками | | **Комунікація** | Через `MessagePort` (`port.postMessage` / `port.onmessage`) | | **Життєвий цикл** | Доки відкритий хоча б один клієнт | | **Немає доступу до DOM** | Тільки обмін повідомленнями | | **Типові сценарії** | Спільний WebSocket, кеш, синхронізація стану, чат між вкладками | ### Типові помилки - Забути `port.start()`. Якщо ви підписуєтеся через `port.addEventListener('message', ...)`, порт лишається неактивним і повідомлення не приходять. Присвоєння `port.onmessage` запускає порт неявно. - Звертатися до `shared.postMessage(...)` замість `shared.port.postMessage(...)`. У SharedWorker метод живе на порту, а не на самому об'єкті воркера. - Розраховувати, що SharedWorker побачить вкладку з іншого origin або з іншого шляху скрипта. Воркер ідентифікується парою origin плюс URL скрипта, інакше це буде вже інший воркер. - Не прибирати закриті порти зі списку `ports`. Порти мертвих вкладок накопичуються, і розсилка починає писати в нікуди. - Плутати SharedWorker із Service Worker. Перший це спільний обчислювальний потік, другий це мережевий проксі з власним життєвим циклом. - Покладатися на SharedWorker як на єдиний механізм синхронізації вкладок. Через нерівну підтримку варто мати запасний варіант, наприклад `BroadcastChannel`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.