Дайджесты новостей
Концептуальная иллюстрация мгновенной селективной активации серверного веб-элемента при приближении курсора без предварительной гидратации.

CycleWire: селективная активация серверного HTML без гидратации

Современный веб попал в парадоксальную ловушку: чтобы показать пользователю простую кнопку «Купить» или выпадающее меню, браузер сначала скачивает сотни килобайтов JavaScript, долго разбирает их и повторно собирает на клиенте то, что сервер уже отрендерил. Этот процесс называется гидратацией. На мощном ноутбуке задержка в доли секунды незаметна, но на обычном смартфоне экран на пару секунд замирает: интерфейс уже виден, однако нажатия игнорируются, пока процессор занят разбором скриптов. Открытая библиотека CycleWire решает эту проблему радикально, предлагая селективную активацию в компактном ядре весом всего 5,2 КБ.

Почему мобильные интерфейсы замирают при загрузке

В классических фреймворках вроде Next.js страница отдается в виде готового HTML, но затем запускается клиентский рантайм. Движок обходит DOM-дерево, строит виртуальные узлы в памяти и привязывает обработчики ко всем элементам. На смартфонах это увеличивает показатель Total Blocking Time (TBT) — время, когда браузер заблокирован и не реагирует на ввод. В тестах на 4-ядерных мобильных процессорах TBT у традиционных фреймворков доходит до 110–150 мс, тогда как у CycleWire держится на нуле. Библиотека полностью отказывается от начального клиентского бандла: сервер отдает чистый HTML, а браузер подтягивает логику только тогда, когда пользователь начинает взаимодействовать с элементом.

Загрузка по намерению: модуль скачивается до клика

Вместо того чтобы ждать физического нажатия и заставлять человека наблюдать спиннер, CycleWire использует упреждающую загрузку по намерению (intent-aware loading). Как только курсор приближается к кнопке (pointerenter), поле ввода получает фокус (focusin) или блок появляется в зоне видимости (IntersectionObserver), движок добавляет в заголовок страницы тег <link rel="modulepreload">.

Браузер незаметно скачивает крошечный модуль действия заранее по HTTP/2, не тратя время на его исполнение. Когда пользователь нажимает кнопку, код уже находится в памяти и срабатывает без сетевой паузы. Если же на устройстве включен режим экономии трафика Save-Data или скорость сети упала до 2G, спекулятивная предзагрузка отключается автоматически.

Декларативная привязка и защита от дублирования запросов

Интерактивность в CycleWire описывается прямо в серверном HTML через декларативные атрибуты, без привязки к конкретному бэкенду:

<!-- Серверная разметка кнопки с предзагрузкой и защитой от повторных нажатий -->
<div class="product-card" id="item-42">
  <h3>Механическая клавиатура</h3>
  <button
    cw-action="cart#add"
    cw-props='{"id": 42, "qty": 1}'
    cw-concurrency="drop"
    cw-preload="intent"
    class="btn-primary"
  >
    В корзину
  </button>
</div>

Атрибут cw-action указывает модуль и экспортируемую функцию, а режим cw-concurrency="drop" игнорирует повторные клики до завершения первого запроса, предотвращая дублирование заказов. Сам обработчик пишется как изолированный асинхронный модуль с поддержкой AbortSignal:

// actions/cart.js — скачивается браузером только по требованию
export async function add({ element, props, signal }) {
  element.disabled = true;
  element.textContent = 'Сохранение...';

  try {
    const response = await fetch('/api/cart/items', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(props),
      signal, // отмена при уходе со страницы или новом действии
    });
    if (!response.ok) throw new Error(`Ошибка: ${response.status}`);

    const data = await response.json();
    element.textContent = 'Добавлено ✓';
    window.dispatchEvent(new CustomEvent('cart:updated', { detail: data }));
  } catch (error) {
    if (signal.aborted) return;
    element.textContent = 'Сбой, повторите';
  } finally {
    element.disabled = false;
  }
}

Архитектурный баланс: кому подходит селективный рантайм

CycleWire сочетает подход Qwik с простотой htmx, но не требует специфического компилятора разметки. Движок модульный: для динамического обновления верстки без перезагрузки страницы подключается модуль cyclewire/morph (2,2 КБ), который точечно синхронизирует узлы реального DOM с сохранением фокуса в формах, не создавая тяжелого виртуального дерева.

Граница применимости решения очевидна: CycleWire не предназначен для сложных приложений уровня графических редакторов или тяжелых таблиц, где сотни компонентов ежесекундно пересчитывают общее клиентское состояние. Зато для интернет-магазинов, медиапорталов и классических веб-сервисов на PHP, Python, Ruby или Go это практичный инструмент, позволяющий получить мгновенный отклик интерфейса и максимальные баллы Core Web Vitals без перевода всего проекта на React.