Розроблення фронтенду сайту на Qwik
Qwik — фреймворк від команди Angular/Wiz з принципово іншою моделлю виконання JavaScript. Замість гідратації (завантажити весь JS → виконати → прикріпити события) Qwik використовує resumability: сервер серіалізує стан приложення прямо в HTML, а браузер «возобновляє» роботу з точки, де зупинився сервер. Без повторного виконання коду при завантаженні сторінки.
Це не чергової DX-експеримент. Це архітектурне рішення для сайтів, де кожні 100ms затримки коштують конверсії.
Модель виконання: чим Qwik відрізняється від всіх інших
Звичайний фреймворк при завантаженні сторінки:
- Браузер отримує HTML (швидко)
- Завантажується весь JS-бандл (повільно на 3G/слабих пристроях)
- Фреймворк запускає гідратацію — знов створює дерево компонентів
- Прикріплюються обробники событій
- Сторінка стає інтерактивною
Qwik:
- Браузер отримує HTML зі серіалізованим станом
- JS взагалі не завантажується до першої взаємодії користувача
- При клику/вводі завантажується тільки той chunk, що потрібен для цієї события
- Стан відновлюється миттєво з HTML
Результат — O(1) завантаження незалежно від розміру приложення. Lighthouse 100 не як виключення, а як базовий стан.
Архітектура Qwik-проекту
Qwik City — мета-фреймворк поверх Qwik (аналог Next.js для React):
src/
routes/
index.tsx # /
products/
index.tsx # /products
[id]/
index.tsx # /products/:id
components/
ui/
layout/
lib/
api.ts
Кожен файл маршруту експортує routeLoader$ для серверних даних та routeAction$ для мутацій — це не хуки, це серверні функції, які оптимізатор виносить в окремі edge-функції.
Ключові примітиви
$-суфікс — символ оптимізатора. Будь-яка функція з $ буде виділена в окремий lazy chunk:
import { component$, useSignal, $ } from '@builder.io/qwik';
export const Counter = component$(() => {
const count = useSignal(0);
// Цей обробник НЕ завантажується при рендері сторінки
// Завантажиться тільки при першому клику
const increment = $(() => {
count.value++;
});
return (
<button onClick$={increment}>
Кліків: {count.value}
</button>
);
});
routeLoader$ — серверні дані з типобезпечністю:
import { routeLoader$ } from '@builder.io/qwik-city';
import type { RequestHandler } from '@builder.io/qwik-city';
export const useProductData = routeLoader$(async ({ params, env }) => {
const apiKey = env.get('API_KEY');
const res = await fetch(`https://api.example.com/products/${params.id}`, {
headers: { Authorization: `Bearer ${apiKey}` }
});
if (!res.ok) throw new Error('Product not found');
return res.json() as Promise<Product>;
});
export default component$(() => {
const product = useProductData();
return (
<article>
<h1>{product.value.name}</h1>
<p>{product.value.description}</p>
</article>
);
});
routeAction$ — обробка форм та мутацій без клієнтського JS:
export const useAddToCart = routeAction$(async (data, { cookie }) => {
const cartId = cookie.get('cartId')?.value;
await addItemToCart(cartId, data.productId, data.quantity);
return { success: true };
}, zod$({ productId: z.string(), quantity: z.number().min(1) }));
Форма працює навіть без JavaScript у браузері — Qwik використовує нативний form submit як fallback.
Оптимізатор та збирання
Qwik використовує Vite + qwikVite plugin. Оптимізатор аналізує AST та автоматично:
- Розділяє код на дрібні chunks по
$-границях - Генерує маніфест
q-manifest.jsonз маппингом символів на файли - Інлайнить серіалізоване стан в HTML через
<script type="qwik/json"> - Застосовує prefetch-стратегії для передзавантаження вірогідних наступних взаємодій
Prefetch можна тонко налаштовувати:
// vite.config.ts
import { qwikVite } from '@builder.io/qwik/optimizer';
import { qwikCity } from '@builder.io/qwik-city/vite';
export default defineConfig({
plugins: [
qwikCity(),
qwikVite({
client: {
outDir: 'dist/client',
},
}),
],
});
Управління станом
Qwik не потребує Redux або Zustand. Вбудовані інструменти:
| Примітив | Призначення |
|---|---|
useSignal<T>() |
Локальне реактивне значення |
useStore<T>() |
Реактивний об'єкт (глибока реактивність) |
useContext / createContextId |
Глобальний контекст |
useResource$ |
Асинхронні дані з SSR-підтримкою |
Для складного глобального стану використовується паттерн з createContextId та useStore:
export const AppContext = createContextId<AppState>('app.state');
export const AppProvider = component$(() => {
const state = useStore<AppState>({
user: null,
theme: 'light',
cart: [],
});
useContextProvider(AppContext, state);
return <Slot />;
});
Тестування та якість
- Vitest — unit-тести компонентів та серверних функцій
- Playwright — e2e тесты, включаючи перевірку що JS не завантажується до взаємодії
- @builder.io/qwik/testing — утиліти для рендеру компонентів в тестах
Метрика для відстеження в CI: розмір initial JS bundle повинен бути < 5 KB (тільки Qwik-завантажувач, без компонентів).
Розгортання
Qwik City підтримує адаптери:
- Cloudflare Pages — edge-функції + глобальний CDN, рекомендований варіант
- Vercel Edge Runtime — без холодного старту
- Node.js / Express — для self-hosted
-
AWS Lambda — через адаптер
@builder.io/qwik-city/adapters/aws-lambda - Static — якщо маршрути не потребують серверної логіки
Терміни реалізації
- Тиждень 1–2: архітектура, роутинг, компонентна база, дизайн-система
- Тиждень 3: серверні завантажувачі, інтеграція з CMS/API, форми
- Тиждень 4: оптимізація prefetch-стратегій, SEO (meta, OpenGraph, структуровані дані)
- Тиждень 5: тестування, налаштування CI/CD, адаптер для розгортання
- Тиждень 6: нагрузочне тестування, фінальна оптимізація Core Web Vitals
Коли Qwik — правильний вибір
Qwik особливо ефективний для контентних сайтів з високою інтерактивністю — e-commerce, медіа, лендинги з формами, портали. Якщо більшість трафіку приходить з мобільних пристроїв в регіонах з повільним інтернетом — різниця в конверсії буде вимірною.
Для внутрішніх інструментів та дашбордів, де користувачі на десктопах з швидким з'єднанням, переваги Qwik менш виражені — SvelteKit або Next.js підійдуть краще.







