WebSocket

FAST Element — это современная библиотека для создания Web Components на JavaScript, предоставляющая удобный синтаксис для декларативного описания компонентов и управления состоянием. Интеграция WebSocket в FAST Element позволяет создавать интерактивные приложения с обновлением данных в реальном времени.

Основы WebSocket

WebSocket — это протокол, обеспечивающий двунаправленное соединение между клиентом и сервером через один TCP-сокет. В отличие от традиционных HTTP-запросов, WebSocket позволяет серверу отправлять данные клиенту без повторных запросов, что критично для чата, мониторинга данных, игровых приложений и других сценариев с высокой частотой обновлений.

Создание соединения на клиенте выглядит следующим образом:

const socket = new WebSocket('ws://localhost:8080');

socket.addEventListener('open', () => {
    console.log('Соединение установлено');
});

socket.addEventListener('message', event => {
    console.log('Получено сообщение:', event.data);
});

socket.addEventListener('close', () => {
    console.log('Соединение закрыто');
});

socket.addEventListener('error', error => {
    console.error('Ошибка WebSocket:', error);
});

Интеграция WebSocket в FAST Element

FAST Element использует классы компонентов, наследуемые от FASTElement. Для интеграции WebSocket создается свойство, хранящее объект соединения, и методы для обработки событий.

Пример компонента с подключением к WebSocket:

import { FASTElement, html, css } from '@microsoft/fast-element';

const template = html`
    <div>
        <h2>Сообщения</h2>
        <ul>
            ${x => x.messages.map(m => html`<li>${m}</li>`)}
        </ul>
        <input type="text" :value=${x => x.input} @input=${x => x.input = x.event.target.value} />
        <button @click=${x => x.sendMessage()}>Отправить</button>
    </div>
`;

const styles = css`
    div { font-family: Arial, sans-serif; }
    ul { list-style: none; padding: 0; }
    li { margin: 4px 0; }
`;

export class ChatComponent extends FASTElement {
    messages = [];
    input = '';
    socket;

    connectedCallback() {
        super.connectedCallback();
        this.initWebSocket();
    }

    disconnectedCallback() {
        super.disconnectedCallback();
        if (this.socket) {
            this.socket.close();
        }
    }

    initWebSocket() {
        this.socket = new WebSocket('ws://localhost:8080');

        this.socket.addEventListener('open', () => {
            console.log('WebSocket соединение открыто');
        });

        this.socket.addEventListener('message', event => {
            this.messages = [...this.messages, event.data];
        });

        this.socket.addEventListener('close', () => {
            console.log('WebSocket соединение закрыто');
        });

        this.socket.addEventListener('error', error => {
            console.error('Ошибка WebSocket:', error);
        });
    }

    sendMessage() {
        if (this.input && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(this.input);
            this.input = '';
        }
    }
}

FASTElement.define({
    name: 'chat-component',
    template,
    styles,
    elementClass: ChatComponent
});

Реактивность данных

FAST Element позволяет реактивно обновлять интерфейс при изменении состояния. Любое присвоение свойства, используемого в шаблоне, автоматически вызывает ререндеринг. В примере выше массив messages обновляется при получении нового сообщения, и <ul> перерисовывается без ручного вмешательства.

Важно использовать новый массив при изменении данных, а не мутировать старый, чтобы FAST Element корректно детектировал изменения:

this.messages = [...this.messages, newMessage];

Управление жизненным циклом WebSocket

FAST Element предоставляет методы жизненного цикла: connectedCallback и disconnectedCallback. Для корректного управления соединением WebSocket их следует использовать следующим образом:

  • Создание соединения в connectedCallback гарантирует, что сокет будет инициализирован после вставки компонента в DOM.
  • Закрытие соединения в disconnectedCallback предотвращает утечки памяти и попытки отправки сообщений в несуществующий компонент.

Обработка ошибок и переподключение

В реальных приложениях соединение может разрываться. Рекомендуется реализовать механизм переподключения:

initWebSocket() {
    this.socket = new WebSocket('ws://localhost:8080');

    this.socket.addEventListener('open', () => {
        console.log('Соединение открыто');
    });

    this.socket.addEventListener('message', event => {
        this.messages = [...this.messages, event.data];
    });

    this.socket.addEventListener('close', () => {
        console.log('Соединение закрыто, переподключение через 3 секунды');
        setTimeout(() => this.initWebSocket(), 3000);
    });

    this.socket.addEventListener('error', () => {
        console.error('Ошибка WebSocket');
        this.socket.close();
    });
}

Поддержка нескольких каналов

Для более сложных приложений часто требуется слушать несколько типов сообщений. Для этого можно использовать объект-хранилище подписок:

this.channels = {
    chat: [],
    notifications: []
};

this.socket.addEventListener('message', event => {
    const data = JSON.parse(event.data);
    if (data.type && this.channels[data.type]) {
        this.channels[data.type] = [...this.channels[data.type], data.payload];
    }
});

Связь с шаблоном

FAST Element позволяет легко привязать динамические данные к DOM. Для каждого канала можно создать отдельный блок в шаблоне:

<ul>
    ${x => x.channels.chat.map(msg => html`<li>${msg}</li>`)}
</ul>
<ul>
    ${x => x.channels.notifications.map(note => html`<li>${note}</li>`)}
</ul>

Итоговые рекомендации по использованию WebSocket в FAST Element

  • Всегда закрывать соединение в disconnectedCallback.
  • Использовать реактивные свойства для автоматического обновления интерфейса.
  • Применять новые массивы при изменении коллекций для корректной детекции изменений.
  • Реализовать обработку ошибок и переподключение.
  • Для сложных приложений использовать каналы сообщений и динамическую привязку к шаблону.

FAST Element совместно с WebSocket позволяет создавать высокореактивные, масштабируемые и интерактивные компоненты с минимальными затратами кода.