Сколько будет WebSockets при двух открытых вкладках?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
WebSockets при двух открытых вкладках
Это отличный вопрос, который проверяет понимание того, как браузер работает с WebSockets и как устроено межвкладочное взаимодействие. Ответ зависит от нескольких факторов.
Базовый ответ: ДВА WebSocket соединения
По умолчанию, каждая вкладка (tab) браузера работает независимо и имеет свой собственный контекст JavaScript. Поэтому если в каждой вкладке открыт одинаковый сайт, то обычно будет ДВА отдельных WebSocket соединения — одно для каждой вкладки.
// Код в каждой вкладке
const socket = new WebSocket("wss://example.com/ws");
socket.onopen = () => {
console.log("Connected");
};
socket.onmessage = (event) => {
console.log("Message:", event.data);
};
Результат:
- Вкладка 1: WebSocket #1 подключена
- Вкладка 2: WebSocket #2 подключена
- Итого: 2 WebSocket соединения
Почему два, а не одно?
- Отдельные контексты — каждая вкладка имеет свой глобальный объект
windowи отдельную переменнуюsocket - Независимые сессии — браузер не синхронизирует состояние WebSockets между вкладками автоматически
- TCP соединения — каждый WebSocket — это отдельное TCP соединение на уровне ОС
Можно ли сделать ОДНОWeb Socket для двух вкладок?
Да! Есть несколько подходов:
1. SharedWorker (самый элегантный)
// shared-worker.js
let socket;
const clients = [];
onconnect = (event) => {
const port = event.ports[0];
clients.push(port);
if (!socket) {
socket = new WebSocket("wss://example.com/ws");
socket.onmessage = (msg) => {
// Отправляем сообщение всем вкладкам
clients.forEach(p => p.postMessage({type: "message", data: msg.data}));
};
}
port.onmessage = (event) => {
if (event.data.type === "send") {
socket.send(event.data.payload);
}
};
port.start();
};
// main.js (в каждой вкладке)
const worker = new SharedWorker("shared-worker.js");
const port = worker.port;
port.onmessage = (event) => {
if (event.data.type === "message") {
console.log("Received:", event.data.data);
}
};
port.start();
function sendMessage(text) {
port.postMessage({type: "send", payload: text});
}
Результат:
- SharedWorker: 1 WebSocket соединение
- Все вкладки обмениваются через SharedWorker
- Итого: 1 WebSocket соединение на несколько вкладок
2. Service Worker + Broadcast Channel
// service-worker.js
let socket;
const connections = new Set();
self.onmessage = (event) => {
if (event.data.type === "INIT") {
if (!socket) {
socket = new WebSocket("wss://example.com/ws");
socket.onmessage = (msg) => {
connections.forEach(client => {
client.postMessage({type: "WS_MESSAGE", data: msg.data});
});
};
}
connections.add(event.ports[0]);
event.ports[0].start();
}
};
// main.js (в каждой вкладке)
const channel = new BroadcastChannel("websocket-channel");
channel.onmessage = (event) => {
if (event.data.type === "WS_MESSAGE") {
console.log("Received from WebSocket:", event.data.data);
}
};
function sendViaWebSocket(message) {
channel.postMessage({type: "SEND", message});
}
3. Простой способ — Broadcast Channel (работает, но требует полного контроля)
// main.js
const channel = new BroadcastChannel("app-sync");
let socket = null;
let isTabWithSocket = false;
// Определяем, будет ли эта вкладка хранить WebSocket
function initializeSocket() {
channel.postMessage({type: "REQUEST_SOCKET_OWNER"});
setTimeout(() => {
if (!isTabWithSocket) {
// Эта вкладка владеет WebSocket
socket = new WebSocket("wss://example.com/ws");
isTabWithSocket = true;
socket.onmessage = (msg) => {
channel.postMessage({type: "WS_MESSAGE", data: msg.data});
};
}
}, 100);
}
channel.onmessage = (event) => {
if (event.data.type === "REQUEST_SOCKET_OWNER" && isTabWithSocket) {
// Отвечаем, что мы владеем сокетом
}
if (event.data.type === "WS_MESSAGE") {
console.log("Message from WebSocket:", event.data.data);
}
};
initializeSocket();
Практические проблемы с двумя WebSockets
Пример проблемы:
// Сервер отправляет сообщение каждому клиенту
// Вкладка 1: получает "Hello"
// Вкладка 2: получает "Hello"
//
// Если вкладки синхронизируют одно и то же состояние,
// может произойти дублирование или рассинхронизация
Когда нужно одно соединение?
- Реал-тайм синхронизация данных — chat, collaborative editor
- Экономия ресурсов — минимизировать сетевые соединения
- Единое состояние приложения — Redux, MobX
- Server push — уведомления должны приходить одному потребителю
Проверка в DevTools
// Посмотреть все открытые соединения
window.performance.getEntriesByType(resource)
.filter(r => r.name.includes(ws))
.forEach(r => console.log(r.name));
// Или через DevTools:
// F12 → Network → Filter → WS (WebSocket)
// Откроешь две вкладки → увидишь 2 соединения
Итоговый ответ
| Сценарий | Количество соединений | Как реализовать |
|---|---|---|
| По умолчанию | 2 WebSocket | Каждая вкладка подключается независимо |
| Оптимизированный | 1 WebSocket | SharedWorker, Service Worker или Broadcast Channel |
| Специальный случай | 3+ WebSocket | Если разные URL или если специально создавать |
Для интервью ожидаемый ответ:
"По умолчанию будет ДВА WebSocket соединения, потому что каждая вкладка имеет независимый JavaScript контекст. Но если нужно оптимизировать и использовать одно соединение, можно использовать SharedWorker или Service Worker для централизованного управления WebSocket, а остальные вкладки будут взаимодействовать с ним через MessagePort или BroadcastChannel."