Qwik — JavaScript-фреймворк, который делает ставку на возобновляемость вместо гидратации. Страница приходит готовым HTML, а JavaScript не выполняет приложение заново: обработчики подгружаются по мере взаимодействия. Первый экран остаётся отзывчивым даже на медленном соединении. Вокруг ядра выстроен Qwik City с файловым роутингом, загрузкой данных и формами. В 2026 году ветка Qwik 1.x стабильна, а Qwik 2 развивается отдельно.

Почему гидратация тормозит

В классическом SSR сервер отдаёт HTML, затем браузер скачивает бандл, выполняет компоненты, восстанавливает состояние и разрешает нажатия. Это гидратация. Чем больше приложение, тем дольше оно «оживает».

Qwik сериализует состояние в HTML и не повторяет выполнение компонентов при загрузке. Теоретически стоимость старта не растёт линейно с размером приложения. На практике всё зависит от объёма сериализованных данных, числа обработчиков и сторонних библиотек, поэтому замеры нужны всегда.

Как работает возобновляемость

В HTML попадает не только разметка, но и ссылки на границы ленивой загрузки. Символ $ создаёт точку разделения: компилятор выносит её в отдельный чанк. Загрузчик qwikloader подгружает обработчик в момент клика.

Состояние должно быть сериализуемым. Замыкания на объекты браузера, классы с методами и большие структуры усложняют возобновление. Держите состояние простым, а тяжёлую логику выносите в server$ или в загрузчики маршрута.

Создание проекта

Понадобится Node.js 18.17 или новее. Мастер создаст проект, установит зависимости и предложит стартовый шаблон. Для разработки используется порт 5173, для предпросмотра production-сборки — 4173.

npm create qwik@latest
cd my-app
npm install
npm run start
npm run preview

Внутри проекта есть каталог src/routes с файловым роутингом, root.tsx и точки входа. Файл index.tsx в папке маршрута экспортирует компонент по умолчанию. Сборка и предпросмотр запускаются через скрипты package.json, поэтому не вызывайте Vite напрямую без необходимости.

Компоненты и сигналы

Компонент объявляется через component$. Сигнал useSignal хранит локальное значение, useStore — объект, а useComputed$ пересчитывает производное значение. Обработчики событий помечаются суффиксом $ и загружаются отдельно.

import { component$, useSignal, useComputed$ } from '@builder.io/qwik';

export const Counter = component$(() => {
  const count = useSignal(0);
  const double = useComputed$(() => count.value * 2);

  return (
    <button onClick$={() => count.value++}>
      {count.value} / {double.value}
    </button>
  );
});

Не оборачивайте каждую мелочь в отдельный компонент: каждая граница $ — это потенциально отдельный чанк и запрос. Группируйте связанную логику и проверяйте, какие чанки реально загружаются в DevTools.

Маршруты, данные и формы

routeLoader$ выполняет загрузку на сервере до рендера и отдаёт сигнал в компонент. routeAction$ обрабатывает отправку формы, в том числе без JavaScript. Компонент Form использует нативную форму, а при включённом JS отправляет данные без перезагрузки страницы.

import { component$ } from '@builder.io/qwik';
import { routeLoader$, Form, routeAction$ } from '@builder.io/qwik-city';

export const usePosts = routeLoader$(async () => {
  return [{ id: 1, title: 'Первый пост' }];
});

export const useAddPost = routeAction$(async (data) => {
  return { ok: true, title: String(data.title) };
});

export default component$(() => {
  const posts = usePosts();
  const action = useAddPost();
  return (
    <>
      <ul>{posts.value.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
      <Form action={action}>
        <input name="title" />
        <button type="submit">Добавить</button>
      </Form>
      {action.value?.ok && <p>Сохранено</p>}
    </>
  );
});

Валидацию делайте на сервере, например через Zod, и не доверяйте полям формы. Для клиентских вызовов есть server$: функция выполняется на сервере.

Сборка и деплой

Адаптер выбирается командой npm run qwik add. Для статического сайта подойдёт адаптер static, для серверного рендеринга — Node или другой поддерживаемый рантайм. После сборки проверьте каталог, который создал адаптер: у разных режимов он отличается.

npm run qwik add static
npm run build
npm run preview

Статическую сборку отдайте через nginx. Хешированные файлы из build кэшируются надолго, а HTML — нет, иначе пользователи останутся на старой версии. Для SSR нужен процесс Node под systemd и reverse proxy.

server {
    listen 80;
    server_name example.com;
    root /var/www/qwik/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /build/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Диагностика и оптимизация

Откройте DevTools и посмотрите, что загружается до первого клика. У хорошо собранного Qwik-приложения не должно быть большого общего бандла: обработчики появляются только после взаимодействия. Проверьте, что HTML содержит сериализованное состояние и загрузчик, а не пустой контейнер.

curl -s https://example.com | grep -o 'qwikloader[^"]*' | head
du -sh dist
find dist -type f -name '*.js' -size +100k -exec ls -lh {} \;
  • сравните размер HTML до и после добавления состояния;
  • проверьте, какие чанки грузятся при клике и при наведении;
  • убедитесь, что тяжёлые библиотеки не попали в начальный бандл;
  • измерьте LCP и INP на мобильном профиле;
  • не используйте useVisibleTask$ для логики, которая может работать на сервере;
  • проверьте, что формы работают при отключённом JavaScript.

Сериализация — источник неожиданного веса. Большой объект в useStore попадёт в HTML и увеличит первый ответ. Дробите состояние и подгружайте детали по требованию.

Когда Qwik не нужен

Для лендинга из трёх экранов или внутренней админки с тяжёлым клиентским состоянием выгода может быть незаметной. Экосистема Qwik меньше, чем у React или Vue, а переход на Qwik 2 потребует проверки зависимостей. Если команда не готова разбираться с сериализацией и границами $, классический SSR может оказаться проще.

Qwik хорошо подходит для контентных сайтов, интернет-магазинов и приложений, где важен быстрый первый экран и много статических страниц. Решение стоит принимать по замерам, а не по обещанию «нулевой гидратации».

Итог

Qwik меняет точку старта: браузер не выполняет приложение целиком, а возобновляет его по мере надобности. Это даёт быстрый первый экран, но требует дисциплины в состоянии. Начните с небольшого проекта, измерьте бандлы и только потом переносите крупный интерфейс.

  • создайте проект через npm create qwik@latest;
  • освойте component$, сигналы и задачи;
  • перенесите загрузку данных в routeLoader$;
  • проверьте формы без JavaScript;
  • соберите production-версию и измерьте чанки;
  • выберите адаптер под свой хостинг и настройте кэш.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте