WebSocket

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

Создание WebSocket соединения

В SkateJS соединение с WebSocket обычно инициализируется внутри жизненного цикла компонента, чаще всего в методе connectedCallback, чтобы оно активировалось при добавлении элемента в DOM:

import { define, props, Component } from 'skatejs';

class LiveFeed extends Component {
  static props = {
    url: props.string
  };

  connectedCallback() {
    super.connectedCallback();
    this.socket = new WebSocket(this.url);

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

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

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

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

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

  handleMessage(data) {
    // Обновление реактивного свойства компонента
    this.data = JSON.parse(data);
  }

  render() {
    return this.data ? `<div>${this.data.message}</div>` : `<div>Загрузка...</div>`;
  }
}

define('live-feed', LiveFeed);

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

Реактивные свойства и обновление данных

SkateJS использует реактивные свойства через props, что позволяет автоматически обновлять DOM при изменении данных. В сочетании с WebSocket это дает возможность реализовать динамическое отображение изменений, приходящих с сервера:

static props = {
  data: props.object
};

При поступлении нового сообщения через WebSocket достаточно обновить this.data, и компонент автоматически перерендерится, отражая актуальное состояние.

Отправка сообщений на сервер

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

sendMessage(msg) {
  if (this.socket && this.socket.readyState === WebSocket.OPEN) {
    this.socket.send(JSON.stringify(msg));
  }
}

Важно проверять состояние сокета через readyState, чтобы избежать ошибок при попытке отправки данных через закрытое соединение.

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

Для надежных приложений необходимо учитывать возможность разрыва соединения. В SkateJS это реализуется через обработку событий close и error, а также через стратегию автоматического переподключения:

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

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

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

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

Такой подход позволяет компоненту сохранять актуальное соединение даже при временных сбоях сети.

Интеграция с другими компонентами

SkateJS поддерживает композицию компонентов, поэтому WebSocket можно использовать как источник данных для нескольких компонентов одновременно. Один компонент может получать поток данных, а дочерние компоненты подписываться на его свойства:

class Dashboard extends Component {
  static props = {
    feedData: props.object
  };

  connectedCallback() {
    super.connectedCallback();
    this.liveFeed = this.querySelector('live-feed');
    this.liveFeed.addEventListener('data-changed', (e) => {
      this.feedData = e.detail;
    });
  }

  render() {
    return `<child-component data="${JSON.stringify(this.feedData)}"></child-component>`;
  }
}

Использование событий SkateJS (CustomEvent) позволяет реализовать реактивное распространение данных от одного компонента к другому без жесткой связи с сервером.

Безопасность и оптимизация

При работе с WebSocket важно учитывать:

  • Валидацию данных: никогда не доверять входящему JSON без проверки структуры и типов.
  • Закрытие соединений: всегда закрывать сокеты при удалении компонента, чтобы предотвратить утечки памяти.
  • Контроль нагрузки: для частого потока данных стоит использовать буферизацию и ограничение обновлений DOM, например, через requestAnimationFrame или debounce.

Пример комплексного использования

Объединение всех принципов:

class ChatWidget extends Component {
  static props = {
    messages: props.array
  };

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

  connect() {
    this.socket = new WebSocket('wss://chat.example.com');

    this.socket.addEventListener('message', (event) => {
      const msg = JSON.parse(event.data);
      this.messages = [...(this.messages || []), msg];
    });

    this.socket.addEventListener('close', () => {
      setTimeout(() => this.connect(), 2000);
    });
  }

  sendMessage(text) {
    if (this.socket.readyState === WebSocket.OPEN) {
      this.socket.send(JSON.stringify({ text }));
    }
  }

  render() {
    return `
      <ul>
        ${this.messages ? this.messages.map(m => `<li>${m.user}: ${m.text}</li>`).join('') : ''}
      </ul>
    `;
  }
}

define('chat-widget', ChatWidget);

В этом примере реализована полноценная функциональность чата с динамическим обновлением сообщений и автоматическим переподключением. Компонент использует реактивные свойства, безопасную отправку сообщений и аккуратное управление соединением.

SkateJS в сочетании с WebSocket позволяет создавать мощные и реактивные веб-компоненты, полностью ориентированные на динамические данные и интерактивность.