Перейти к содержимому

3. Resumability vs Hydration

Resumability — это центральная концепция Qwik, которая принципиально отличает его от всех остальных фреймворков. Чтобы по-настоящему понять resumability, нужно детально разобраться с тем, как работает традиционная гидратация и почему Qwik предлагает принципиально другой подход.

Когда пользователь заходит на React/Vue/Angular сайт с SSR:

Фаза 1: Сервер рендерит HTML
───────────────────────────────────────────────
const html = renderToString(<App />)
// Результат: <div id="root"><button>Count: 0</button></div>
// Состояние потеряно! Сервер не знает что это Counter с value=0
Фаза 2: Браузер получает HTML (страница выглядит готовой)
───────────────────────────────────────────────
// Пользователь ВИДИТ кнопку, но она НЕ РАБОТАЕТ
// onClick ещё не привязан!
Фаза 3: Браузер загружает JS (200-500 КБ)
───────────────────────────────────────────────
<script src="/bundle.js"></script> // Весь код приложения!
Фаза 4: React гидратирует (воссоздаёт то, что уже сделал сервер)
───────────────────────────────────────────────
ReactDOM.hydrateRoot(document, <App />)
// React обходит ВСЁ дерево компонентов заново
// Это избыточная работа — HTML уже есть!
Фаза 5: Страница становится интерактивной ✅

Ключевая проблема: браузер проделывает работу, которую уже сделал сервер. Это называется «дублирование работы».

Почему нельзя просто не делать гидратацию?

Заголовок раздела «Почему нельзя просто не делать гидратацию?»

Без гидратации браузер не знает:

  • Какие функции привязаны к каким DOM-элементам
  • Какое состояние у компонентов
  • Какие компоненты существуют и где они начинаются/заканчиваются

React не может передать эту информацию в HTML, потому что функции JavaScript не сериализуемы.

Qwik решает проблему, сохраняя все необходимые данные прямо в HTML:

<!-- Обычный React SSR HTML -->
<div id="root">
<button>Count: 0</button>
</div>
<!-- Обработчик события потерян! -->
<!-- Qwik SSR HTML -->
<div>
<button
on:click="./chunk-abc.js#Counter_button_onClick_0BkQ6"
q:id="1">
Count: 0
</button>
</div>
<!-- Сериализованное состояние -->
<script type="qwik/json">
{
"objs": [0, "#1"],
"subs": [["3 #1 count 0"]]
}
</script>
1. Ссылки на обработчики событий (как URL на чанки)
on:click="./chunk-abc.js#handler_name"
2. Состояние компонентов (через useSignal/useStore)
<script type="qwik/json">{...}</script>
3. Дерево компонентов (через q:id атрибуты)
q:id="0", q:id="1", q:id="2"...
4. Связи между состоянием и DOM
Какой сигнал управляет каким элементом
Фаза 1: Сервер рендерит HTML + сериализует состояние
───────────────────────────────────────────────────
HTML + <script type="qwik/json"> = полное описание приложения
Фаза 2: Браузер получает HTML
───────────────────────────────────────────────────
✅ Страница СРАЗУ интерактивна!
Qwik регистрирует один глобальный обработчик событий.
Фаза 3: Пользователь кликает кнопку
───────────────────────────────────────────────────
Qwik runtime (~1 КБ) перехватывает событие.
Читает URL обработчика из on:click атрибута.
Загружает нужный чанк (только этот обработчик!).
Десериализует состояние из qwik/json.
Выполняет обработчик.
Обновляет только нужные DOM-узлы.

QRL (Qwik Resource Locator) — это URL-ссылка на функцию вместо самой функции:

// React — функция передаётся напрямую (не сериализуемо)
<button onClick={() => {
// Весь этот код в начальном бандле
const result = heavyComputation();
setState(result);
}}>
Click
</button>
// Qwik — функция преобразуется в QRL (URL)
<button onClick$={() => {
// Этот код в ОТДЕЛЬНОМ чанке
// Загружается только при клике!
const result = heavyComputation();
state.value = result;
}}>
Click
</button>

Символ $ в Qwik — это магический суффикс, который говорит оптимизатору: «вынеси эту функцию в отдельный чанк».

React: пользователь кликает кнопку
┌─ Все обработчики уже загружены (гидратация) ─┐
│ handler уже в памяти → выполняется сразу │
│ Но: 2-5 секунд ожидания ДО этого момента │
└──────────────────────────────────────────────┘
Qwik: пользователь кликает кнопку
┌─ Обработчик загружается лениво ──────────────┐
│ 1. Перехват события (~0 мс) │
│ 2. Загрузка чанка (~50-100 мс) │
│ 3. Выполнение (~1 мс) │
│ Но: страница интерактивна СРАЗУ │
└──────────────────────────────────────────────┘