Работа с WebSocket соединениями

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


Создание WebSocket-соединения через эффекты

Hyperapp использует эффекты (effects) для побочных действий, таких как сетевые запросы. Для WebSocket создается отдельный эффект, который инициализирует соединение и обрабатывает события onopen, onmessage, onclose и onerror.

Пример эффекта для WebSocket:

const wsEffect = (url, actions) => (dispatch) => {
  const socket = new WebSocket(url);

  socket.ono pen = () => {
    dispatch(actions.connected());
  };

  socket.onmess age = (event) => {
    const data = JSON.parse(event.data);
    dispatch(actions.messageReceived(data));
  };

  socket.oncl ose = () => {
    dispatch(actions.disconnected());
  };

  socket.oner ror = (error) => {
    dispatch(actions.error(error));
  };

  return () => socket.close();
};

Ключевые моменты:

  • Эффект возвращает функцию закрытия соединения, что позволяет корректно очищать ресурсы при размонтировании компонентов.
  • События WebSocket вызывают соответствующие действия Hyperapp, обеспечивая реактивное обновление состояния.
  • Все действия обрабатывают данные строго через действия (actions), сохраняя поток данных предсказуемым.

Интеграция эффекта в приложение Hyperapp

Состояние приложения содержит поля, отражающие текущее состояние WebSocket:

const state = {
  connected: false,
  messages: [],
  error: null,
};

Действия для управления состоянием:

const actions = {
  connected: () => ({ connected: true }),
  disconnected: () => ({ connected: false }),
  messageReceived: (message) => (state) => ({
    messages: [...state.messages, message],
  }),
  error: (err) => ({ error: err }),
};

Инициализация эффекта при старте приложения:

import { app } from "hyperapp";

app({
  init: [state, wsEffect("wss://example.com/socket", actions)],
  state,
  actions,
  view: (state) => (
    <div>
      <h2>Статус соединения: {state.connected ? "онлайн" : "офлайн"}</h2>
      <ul>
        {state.messages.map((msg) => <li>{msg.text}</li>)}
      </ul>
    </div>
  ),
});

Отправка данных через WebSocket

Для отправки сообщений используется отдельное действие, которое взаимодействует с объектом WebSocket. Его удобно хранить в замыкании эффекта:

const wsEffect = (url, actions) => (dispatch) => {
  const socket = new WebSocket(url);

  socket.ono pen = () => dispatch(actions.connected());
  socket.onmess age = (event) => dispatch(actions.messageReceived(JSON.parse(event.data)));
  socket.oncl ose = () => dispatch(actions.disconnected());
  socket.oner ror = (error) => dispatch(actions.error(error));

  const sendMessage = (message) => {
    if (socket.readyState === WebSocket.OPEN) {
      socket.send(JSON.stringify(message));
    }
  };

  actions.sendMessage = sendMessage;

  return () => socket.close();
};

Таким образом, действия Hyperapp остаются чистыми, а логика отправки сообщений инкапсулирована в эффекте.


Обработка повторного подключения

Для приложений с реальным временем критически важно обрабатывать разрывы соединения. Реализуется это через рекурсивные таймеры и повторные вызовы эффекта:

const reconnectEffect = (url, actions, interval = 5000) => (dispatch) => {
  let socket;

  const connect = () => {
    socket = new WebSocket(url);

    socket.ono pen = () => dispatch(actions.connected());
    socket.onmess age = (event) => dispatch(actions.messageReceived(JSON.parse(event.data)));
    socket.oncl ose = () => setTimeout(connect, interval);
    socket.oner ror = (error) => dispatch(actions.error(error));
  };

  connect();

  return () => socket.close();
};

Особенности:

  • Повторное подключение осуществляется через таймер, что предотвращает бесконечные попытки при недоступном сервере.
  • Можно гибко управлять интервалом и условиями повторного подключения.
  • Поддерживается реактивное обновление состояния при каждом изменении соединения.

Организация потоков данных

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

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

Советы по производительности и безопасности

  • Закрывать WebSocket при размонтировании компонентов, чтобы избежать утечек памяти.
  • Ограничивать частоту отправки сообщений, используя debounce или throttle.
  • Проверять состояние socket.readyState перед отправкой данных.
  • Использовать протокол wss:// для защищенного соединения.

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

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

  • Разделять логику обработки различных типов данных.
  • Обеспечивать независимое подключение и переподключение.
  • Легко масштабировать приложение, добавляя новые каналы без изменения основной логики.

Эта структура обеспечивает реактивное управление WebSocket-соединениями, позволяет поддерживать чистоту действий и легко масштабируется под сложные сценарии реального времени.