Інтеграція WebSockets з сучасними JavaScript фреймворками

(Всеосяжний — можливо, надмірно балакучий — посібник, натхненний мною)

pic

Ласкаво просимо, дорогі читачі (колеги по захопленню JavaScript, якщо хочете). Сьогодні ми заглибимося в містичний світ WebSockets і розглянемо, як їх можна граціозно — а іноді й з труднощами — впровадити в наші улюблені сучасні JavaScript-фреймворки. Так, ми говоримо про React, Angular, Vue, Svelte та про тих, хто може незапрошеним з’явитися на вечірці (дивлюсь на тебе, Alpine.js).

Перш ніж ми перейдемо до технічних складнощів (які, обіцяю, не є такими складними, як це може здаватися після кількох хвилин боротьби), дозвольте мені невелику відступну: колись я так боявся впроваджувати функції реального часу, що вдавався до використання опитувань, намагаючись переконати себе, що це справжній реальний час (і намагався тримати серйозне обличчя, коли це говорив — не вдалося). Це було схоже на те, як стояти на краю басейну, кажучи, що я вже «плаваю», тому що іноді мочив ноги. Але як тільки я відкрив для себе чудеса WebSockets, я зрозумів, що справжнє плавання, хоча й іноді лякає, набагато освіжаюче.

Отже, якщо ви також вдавалися до фальшивого реального часу, використовуючи серверні перевірки на setInterval кожні дві секунди (ми всі це робили — без сорому), цей блог для вас. А якщо ви вже рок-зірка в реальному часі, читайте далі. Можливо, є кілька жартів, щоб вас розважити.

Давайте зануримось!

Що таке WebSockets?

Почнемо з основ (адже ми любимо занурюватися в них — як хом’як в свіжу пачку дерев’яних стружок).

Визначення: WebSocket — це стійкий двосторонній канал зв’язку між клієнтом та сервером через одне TCP з’єднання. Це, по суті, красиве пояснення фрази «клієнт та сервер можуть спілкуватися в реальному часі без необхідності встановлювати нові з’єднання кожні п’ять мілісекунд».

Це не те ж саме, що HTTP, де клієнт (як ввічливий відвідувач ресторану) повинен постійно піднімати руку, щоб запитати, чи може сервер передати сіль (і сервер може відповісти — іноді через кілька годин). Замість цього WebSockets дозволяють клієнту та серверу вільно спілкуватися, ніби вони сидять на барному стільці десь, обмінюючись історіями про «старі добрі часи, коли JavaScript був призначений тільки для перевірки форм». Я не можу підтвердити, що це справжня розмова, але ви розумієте, про що я.

У стандартній моделі HTTP:

  1. Клієнт: «Привіт, сервере, як справи? Є для мене нові дані?»
  2. Сервер: «Ні, не зараз. Перевір ще через хвилинку.»
  3. Клієнт (трохи пізніше): «А тепер є нові дані для мене?»
  4. Сервер: «Все ще ні, вибач.»
    (Повторюється без кінця.)

З WebSocket:

  1. Клієнт: «Привіт, давай відкриємо з’єднання WebSocket.»
  2. Сервер: «Чудово. Спілкуймося, коли є що сказати.»
    (Тепер обидві сторони можуть говорити, коли є що сказати. Вражаюче, правда?)

Чому WebSockets важливі в 2025 році і в подальшому

Ера гіпер-зв’язуваного всього

Ми живемо в еру, коли ваш холодильник може написати вам повідомлення (використовуючи з’єднання в реальному часі), щоб повідомити, що у вас закінчився апельсиновий сік. Наші тостери скоро будуть автоматично твітити кожен раз, коли вони підсмажують скибочку хліба. Обсяг даних у реальному часі вражає — і фреймворки повинні встигати за темпами.

Наприклад:

  • Чат-програми: реальне повідомлення є критичним для чату.
    Якщо ваш чат-аплікація оновлює повідомлення тільки після 5-секундного оновлення, ви не зможете конкурувати з великими гравцями.
  • Онлайн-ігри: Якщо ви будуєте спільну гру в Тетріс (я ще маю зробити це одного дня — просто для сміху), вам потрібно миттєво обмінюватися даними.
  • Живі панелі моніторингу: Біржові котирування, реальний аналітичний моніторинг для вебсайтів, спортивні результати або навіть той живий опитування, яке ви робите на вашому стрімі, щоб вирішити, чи носити пандовий костюм чи костюм єдинорога.

Фактор продуктивності

WebSockets означають менше накладних витрат. Одне стійке з’єднання краще за сотні повторених HTTP-запитів. Це ефективніше, цікавіше і більше відповідає загальному принципу "менше — це більше" (принцип, в якому я частково вірю, враховуючи довжину цього поста).

Основні поняття, які слід пам’ятати

1. Повний дуплекс
Клієнт і сервер можуть спілкуватися без очікування один на одного. Це як різниця між рацією (де ви натискаєте для того, щоб говорити) і телефонним дзвінком (де ми можемо перебивати один одного в середині речення, але сподіваючись на ввічливість).

2. Оновлення з HTTP
WebSockets починаються з HTTP-рукопожаття — а потім швидко переходять до більш прямого зв’язку. Це як швидкі побачення: ви швидко зустрічаєтеся через HTTP, і якщо ви обидва згодні, переходите до більш глибокого зв’язку (WebSocket).

3. На основі подій
Якщо вам подобаються події в JavaScript — а хто з нас не відчуває дивний захват, додаючи прослуховувачі подій (Event Listener)? — вам буде комфортно. Типові події — onopen, onmessage, onerror, onclose.

4. Безпека
wss:// — це захищена форма WebSockets, подібно до https:// для стандартного HTTP. Якщо ви маєте справу з конфіденційною інформацією, а, ймовірно, маєте, пам’ятайте використовувати wss://, коли це можливо.

5. Обробка помилок
Реальний час може означати реальні проблеми. Вам потрібно обробляти непередбачувані відключення, часткові повідомлення або навіть збої вашого сервера посеред трансляції. Як зазвичай, тестування — ваш друг. (Так, для тих, хто практикує TDD, можете хвалитися.)

Інтеграція WebSockets з React

Отже, давайте поговоримо про великий «риб» у морі: React. React зосереджений на компонентах, станах та пропсах (і часом на стресі з приводу hooks vs. classes).

Крок 1: Налаштування підключення WebSocket

  1. Відкрийте з’єднання: У типовому React додатку, ви можете відкрити з’єднання WebSocket у компоненті верхнього рівня (наприклад, App.js) або в спеціалізованому модулі утиліт.
  2. Використовуйте React Hooks: Якщо ви відчуваєте себе впевнено (і сучасно), ви можете використовувати useEffect для створення, керування та очищення вашого підключення WebSocket.
import React, { useState, useEffect } from 'react';  

function App() {  
 const [messages, setMessages] = useState([]);  

 useEffect(() => {  
 const socket = new WebSocket('wss://yourserver.com/socket');  

 socket.onopen = () => {  
 console.log('Connected to the WebSocket server');  
 socket.send('Hello from React!');  
 };  

 socket.onmessage = (event) => {  
 setMessages((prev) => [...prev, event.data]);  
 };  

 socket.onerror = (error) => {  
 console.error('WebSocket Error:', error);  
 };  

 socket.onclose = () => {  
 console.log('WebSocket connection closed');  
 };  

 // Cleanup  
 return () => {  
 socket.close();  
 };  
 }, []);  

 return (  
   <div>  
     Messages  
     {messages.map((msg, idx) => (  
       <div key={idx}>{msg}</div>  
     ))}  
   </div>  
 );  
}  

export default App;  

Крок 2: Керування станом та повідомленнями

Унікальний потік даних у React означає, що ваші повідомлення зазвичай зберігаються в змінній стану. Коли нове повідомлення надходить, ви оновлюєте масив (або об’єкт) у стані, що викликає повторний рендер. Користувач бачить нове повідомлення в реальному часі (ура!).

Крок 3: Обробка крайніх випадків

  • Розмонтування компонента: Завжди пам’ятайте закрити WebSocket під час очищення.
    Інакше у вас можуть з'явитися "привидні" з'єднання, що приведуть до витоків пам'яті або ваша консоль для розробників буде кричати на вас з повідомленнями про помилки.
  • Логіка перепідключення: Якщо вашому додатку потрібна висока доступність, реалізуйте спроби перепідключення. Інший підхід — це використання бібліотек, як-от Socket.IO, які елегантно обробляють перепідключення (але це окрема розмова).

Angular і WebSockets: Плавний (але не ідеальний) союз

Мені подобається структура Angular — це як добре організована шафа з позначеними коробками для всього. Але іноді вона може здатися трохи важкою. Хороша новина полягає в тому, що Angular підтримує WebSockets без проблем, хоча вам, можливо, доведеться використовувати сервіси або Observables (RxJS), щоб усе працювало добре.

Крок 1: Створіть сервіс Angular

В Angular ви зазвичай створюєте сервіс (наприклад, websocket.service.ts), щоб обробляти всі деталі підключення та управління з’єднанням.

import { Injectable } from '@angular/core';  
import { Observable, Subject } from 'rxjs';  

@Injectable({  
 providedIn: 'root'  
})  
export class WebsocketService {  
 private socket: WebSocket;  
 private messagesSubject = new Subject();  

 connect(url: string): void {  
 this.socket = new WebSocket(url);  

 this.socket.onopen = () => {  
 console.log('WebSocket connection established.');  
 };  

 this.socket.onmessage = (event) => {  
 this.messagesSubject.next(event.data);  
 };  

 this.socket.onerror = (error) => {  
 console.error('WebSocket Error:', error);  
 };  

 this.socket.onclose = () => {  
 console.log('WebSocket connection closed.');  
 };  
 }  

 sendMessage(msg: string): void {  
 if (this.socket && this.socket.readyState === WebSocket.OPEN) {  
 this.socket.send(msg);  
 }  
 }  

 getMessages(): Observable {  
 return this.messagesSubject.asObservable();  
 }  
}

Крок 2: Використовуйте сервіс у компоненті

У вашому компоненті (наприклад, app.component.ts), інжектуйте сервіс і підпишіться на повідомлення.

import { Component, OnInit } from '@angular/core';  
import { WebsocketService } from './websocket.service';  

@Component({  
 selector: 'app-root',  
 template: `  

Angular & WebSockets
{{ msg }}
Send    
    `   })   export class AppComponent implements OnInit {    messages: string[] = [];    newMessage: string = '';       constructor(private wsService: WebsocketService) {}       ngOnInit() {    this.wsService.connect('wss://yourserver.com/socket');    this.wsService.getMessages().subscribe(msg => {    this.messages.push(msg);    });    }       send() {    this.wsService.sendMessage(this.newMessage);    this.newMessage = '';    }   } ```

## Крок 3: Observables і магія Angular

Angular заохочує використання RxJS Observables. Це фактично потоки даних, на які можна підписуватися. Це чудово працює з WebSockets, оскільки вони передають дані в реальному часі. Крім того, ви можете ланцюжити оператори для фільтрації, відображення або трансформації вхідних даних перед тим, як вони потраплять до компонента. (RxJS може здатися магією — не лякайтесь, якщо вам почнуть снитися метафори потоків.)

## Підхід Vue: Елегантно, м'яко — і повно сюрпризів

Ах, **Vue**. Фреймворк, який, ймовірно, найпростіший для освоєння (і часом важкий для відмови, коли ви закохуєтесь у його реактивність). Інтеграція WebSockets тут також досить проста.

## Крок 1: Використовуйте хоки життєвого циклу

Якщо ви використовуєте Vue 3, у вас є Composition API з `onMounted`, `onUnmounted` тощо. Або ви можете залишитися з Options API і використовувати `mounted`, `beforeUnmount` тощо.
Незалежно від підходу, він схожий.

**З Composition API**:

import { ref, onMounted, onUnmounted } from 'vue';

export default {
setup() {
const messages = ref([]);
let socket;

onMounted(() => {
socket = new WebSocket('wss://yourserver.com/socket');
socket.onopen = () => {
console.log('Connected to the WebSocket server');
socket.send('Hello from Vue!');
};
socket.onmessage = (event) => {
messages.value.push(event.data);
};
socket.onerror = (error) => {
console.error('WebSocket Error:', error);
};
socket.onclose = () => {
console.log('WebSocket connection closed');
};
});

onUnmounted(() => {
if (socket) {
socket.close();
}
});

const sendMessage = (msg) => {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(msg);
}
};

return {
messages,
sendMessage
};
}
};
```

Крок 2: Прив’язка до шаблону

У вашому шаблоні:


Крок 3: Реактивність

Найкраща частина: реактивність Vue бере на себе всю важку роботу. Якщо нове повідомлення надходить, воно автоматично відображається в DOM. Без ручного setState, без спеціальних спостерігачів. Просто тепле обіймання реактивних даних.

Підхід Svelte: Елегантно, м’яко — і повно сюрпризів

Ах, Vue. Фреймворк, який, ймовірно, найпростіший для освоєння (і часом важкий для відмови, коли ви закохуєтесь у його реактивність). Інтеграція WebSockets тут також досить проста.

Крок 1: Розділ скрипту

В Svelte ви часто поміщаєте вашу логіку в розділ `


## Крок 2: Розмітка

Svelte & WebSockets
{#each messages as msg}
{msg}
{/each}
Send ```

Крок 3: Реактивні оновлення

Svelte автоматично перерисовує при зміні локальних змінних. Отже, як тільки повідомлення буде додано до масиву messages, ваш інтерфейс одразу оновиться. Без клопотів, без складної обробки життєвого циклу. Девіз Svelte — "Кібернетично покращені веб-додатки" — може звучати трохи загрозливо, але на практиці це просто чудово.

Поширені проблеми (і яма вічних помилок під час виконання)

Тепер, коли ми пройшли через всі фреймворки, давайте поговоримо про підводні камені:

  1. Забування закрити з’єднання: Якщо ви переходите на іншу сторінку або демонтуєте компонент, завжди закривайте WebSocket. Інакше ви отримаєте витоки пам'яті і дивні помилки.
  2. Нечітка реалізація на стороні сервера: Сервісна сторона повинна правильно обробляти handshake WebSocket і подальший потік даних. Одна неправильна строка може залишити ваш фронтенд у стані плутанини.
  3. Безпека: Якщо ви працюєте з чутливими даними користувачів, завжди використовуйте wss://.
  4. Токени аутентифікації або підхід на основі JWT: Використовуйте токени аутентифікації або підхід на основі JWT, щоб забезпечити, що підключаються тільки дійсні клієнти.
  5. Обробка змін мережі: Якщо мережа користувача нестабільна, ваше з’єднання може бути втрачено. Реалізуйте логіку повторного підключення або скористайтеся бібліотеками, які роблять це автоматично.
  6. Плутанина версій: Якщо ви використовуєте Socket.IO або спеціалізовану бібліотеку, переконайтеся, що версії клієнта та сервера співпадають. Невідповідність версій — це як спробувати скласти пазл з частин від різних наборів.

Кращі практики: тому що ми дійсно вчимося на своїх помилках (іноді)

  • Використовуйте інструмент управління станом: Якщо ваш додаток великий, подумайте про використання Redux (для React), NgRx (для Angular), Vuex (для Vue 2) або Pinia (для Vue 3), або вбудовані магазини в Svelte. Централізація даних WebSocket допоможе уникнути хаосу в коді.
  • Ізолюйте логіку: Створіть окрему службу або утиліту для WebSocket. Не розкидайте виклики new WebSocket() по всіх компонентах.
  • Дотримуйтеся принципу DRY: Якщо вам потрібна специфічна логіка обробки повідомлень, використовуйте одну і ту ж функцію у всьому додатку або зберігайте її в одному місці. Не перевинаходьте колесо в кожному файлі.
  • Гладка обробка помилок: Виводьте користувачам зрозумілі повідомлення про помилки, якщо з'єднання було втрачено. Можливо, чергуйте повідомлення, поки немає з'єднання, а потім повторно відправляйте їх, коли воно відновиться. Користувачі цінують, коли їхні дані не втрачаються.
  • Тестування: Використовуйте моки або тестові сервери, щоб переконатися, що ваша логіка WebSocket надійна. Тести на кінцевих точках можуть допомогти виявити проблеми, які не завжди видно під час юніт-тестування.

Реальні варіанти використання: де ідеї перетворюються на реальність

1. Інструменти для співпраці в реальному часі

Уявіть додатки, схожі на Google Docs. Коли кілька користувачів одночасно редагують документ, зміни транслюються в реальному часі. WebSockets забезпечують миттєве відображення всіх оновлень.

2. Спортивні трансляції в реальному часі

Наприклад, ESPN використовує реальні потоки для оновлень результатів матчів. Якщо у вас є додаток, який відстежує крикет або керлінг (до речі, цей спорт значно недооцінений), ви захочете, щоб ці оновлення відображались плавно.

3. Онлайн-ігри

Багатокористувацькі ігри — від простих вікторин до складних ММОРПГ — залежать від даних в реальному часі. Неможливо грати в "хто швидше вдарить" з використанням опитувань кожні 5 секунд.

4. Панелі управління або аналітика

Якщо у вас є панель адміністратора, яка повинна відображати реальні дії користувачів, статуси системи або котирування акцій, WebSockets дозволяють підтримувати все актуальним без витрат ресурсів серверу через повторювані запити.

5. Чати та повідомлення

Очевидно, але важливо. Чат — це основа WebSockets. Якщо ви створюєте клон Slack або будь-який інший додаток для групових повідомлень, WebSockets стануть вашими новими найкращими друзями.

Моя особиста історія: Мій шлях від опитувань до реального часу

(Отже, час для історії, друзі. Увімкніть потріскування каміна на фоні…)

Коли я почав працювати з реальними додатками, я спочатку вважав, що “реальний час” означає просто “дуже часті опитування”. Тому я налаштував setInterval, щоб звертатися до сервера кожну секунду — як гіперактивний щеня, що тягне за собою власника. Це рішення працювало, але було не таким елегантним, як носити шкарпетки з шльопанцями (немає засудження, якщо це ваш стиль).

Однак одного дня я відкрив для себе WebSockets, і це було як крок у сучасну еру. Більше не було постійних пінгів до сервера або марної витрати пропускної здатності. Раптом мій сервер отримав можливість дихати, а мій фронтенд оновлювався швидше, ніж я міг сказати "затримка". Це було магічно, ніби я знайшов чіт-код для створення чатів, пуш-сповіщень і інструментів для колективного редагування. Моя найбільша жалоба — що не прийняв WebSockets раніше — і що я не навчився, що скибочки сиру не повинні бути в серверних стелажах (не питайте).

Отже, якщо ви все ще сумніваєтесь (і, так, іноді на паркані комфортно сидіти), стрибайте через нього.
WebSockets можуть змінити підхід до роботи з даними в реальному часі — і ви ніколи більше не подивитеся на ваш підхід із використанням setInterval без маленького ностальгічного крінжу.

Поширені запитання: Версія WebSocket

Ось кілька поширених запитань — зібраних із тих моментів, коли ми всі чухали голови. Сподіваюся, ці короткі відповіді допоможуть вам заощадити трохи часу на пошук в Google:

  1. Q: Чи справді мені потрібні WebSockets, якщо я можу просто опитувати сервер кожну секунду?
    A: Опитування підходить для легких або нечастих оновлень. Але це витрачає пропускну здатність і може бути повільним для реальних потреб у реальному часі. WebSockets дозволяють даним плавно текти в обох напрямках, зменшуючи накладні витрати та надаючи миттєві оновлення.
  2. Q: У чому різниця між WebSockets та Socket.IO?
    A: WebSockets — це протокол, підтримуваний браузерами. Socket.IO — це бібліотека, побудована на WebSockets (з можливістю використання fallback), яка спрощує комунікацію в реальному часі та додає функції, такі як автоматичне підключення.
  3. Q: Чи є WebSockets безпечними?
    A: Вони можуть бути. Використовуйте wss:// (захищений WebSocket), щоб шифрувати дані під час передачі, і реалізуйте перевірки аутентифікації/авторизації на стороні сервера. Завжди ставте безпеку на перше місце.
  4. Q: Чи можна використовувати WebSockets з SSR (Server-Side Rendering)?
    A: Так. Але зазвичай, SSR обробляє початковий рендер. Після цього клієнт може відкрити з’єднання WebSocket. Просто зверніть увагу на те, як ви структуруєте свій код, щоб сервер не намагався відкрити WebSocket під час етапу SSR.
  5. Q: Як обробляти великі обсяги даних?
    A: Якщо ви часто відправляєте великі обсяги даних, подумайте про їх стиснення або використання більш ефективного формату даних, наприклад, Protocol Buffers або MessagePack. Однак простий JSON часто підходить для малих і середніх навантажень.
  6. Q: Чи можна поділитися з’єднаннями WebSocket між кількома вкладками?
    A: Так, через такі технології, як SharedWorker або service worker, можна поділити одне з’єднання WebSocket між кількома вкладками браузера. Але для простіших додатків окреме з’єднання для кожної вкладки може бути простішим у реалізації (хоча більш витратним по ресурсах).
  7. Q: Чи дорого підтримувати безліч з’єднань WebSocket?
    A: Кожне з’єднання використовує деякі ресурси, але зазвичай це не надто важко, якщо ви не обробляєте десятки тисяч або більше з’єднань. Ключ до успіху — налаштувати серверну інфраструктуру для масштабування відповідно.
  8. Q: Чи потрібно закривати з’єднання, коли користувач неактивний?
    A: Можливо. Якщо ваш додаток не потребує оновлень у реальному часі, коли користувач відсутній, ви можете закрити з’єднання або призупинити оновлення. Але якщо ви закриєте його, ви втратите функціональність реального часу, поки користувач не повернеться.
  9. Q: Як я можу налагоджувати WebSockets?
    A: У Chrome DevTools (і в більшості сучасних браузерів) можна побачити активність мережі для WebSockets. Плюс, консольні логи — ваші друзі. Для просунутого налагодження можна використовувати спеціалізовані інструменти або проксі на базі Node.js для перевірки трафіку WebSocket.
  10. Q: Чи можна використовувати WebSockets в React Native або мобільних додатках?
    A: Абсолютно. React Nativeбільшість мобільних фреймворків) підтримують WebSockets. Інтеграція схожа — просто зверніть увагу на мобільні мережеві умови та логіку повторного підключення.

Висновок: Створення (реальних) хвиль

От і все, друзі — епічний тур по WebSockets у сучасних JavaScript фреймворках. Будь ви вірним користувачем React, шанувальником Angular, любителем Vue або фанатом Svelte (або ж розбещеним розробником, який використовує всі фреймворки одночасно — чому б і ні?), WebSockets дійсно можуть змінити спосіб взаємодії ваших додатків з світом. Реальний час — це майбутнє — і сучасність — вебу, а навичка навчитися використовувати її буде тією, за яку ви подякуєте собі (коли не дякуватимете каві за те, що вона тримає вас у тонусі).

І пам’ятайте: ми всі коли-небудь робили помилку з опитуванням.
У цьому немає нічого соромного. Це лише ще один крок на шляху до справжнього розуміння сили постійної, двосторонньої комунікації. Прийміть реальний час (а можливо, зробіть реальний таймлапс з того тосту на новому підключеному тостері — тільки для чистого задоволення).

Гасло: "Кодувати легко… сказав ніхто, хто дійсно кодив — але ми все одно це робимо!"

(Ось тут я зазвичай залишаю вас з підморгуванням і усмішкою, сподіваючись, що ви зробите правильні вибори в Інтернеті.)

До наступного разу — йдіть вперед, відкривайте ці сокети і тримайте ваш пропускну здатність легкою та безперешкодною!

"Нехай ваш код буде без помилок, ваші WebSockets стабільні, а кава завжди гаряча." Напишіть мені, якщо що...>

Перекладено з: Integrating WebSockets with Modern JavaScript Frameworks

Leave a Reply

Your email address will not be published. Required fields are marked *