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

14. Оптимизация и lazy-loading

Одна из главных суперсил Qwik — автоматическая оптимизация бандла и ленивая загрузка. Понимание того, как работает оптимизатор Qwik, помогает писать максимально быстрые приложения.

Каждый раз, когда вы добавляете $ к функции в Qwik, оптимизатор автоматически:

  1. Выносит функцию в отдельный файл-чанк
  2. Создаёт QRL (URL-ссылку) на этот чанк
  3. Загружает чанк только при необходимости
// Что вы пишете:
export const Counter = component$(() => {
const count = useSignal(0);
return (
<button onClick$={() => count.value++}>
{count.value}
</button>
);
});
// Что создаёт оптимизатор (концептуально):
// chunk-counter.js — компонент
export const Counter = component$(qrl('./chunk-counter_onClick.js', 'Counter_onClick'));
// chunk-counter_onClick.js — обработчик (отдельный файл!)
export const Counter_onClick = () => { count.value++; };
Символ $ → Qwik Optimizer → Отдельный чанк
──────────────────────────────────────────────────────
component$() → chunk-comp.js → Загружается при рендере
onClick$() → chunk-handler.js → Загружается при клике
useTask$() → chunk-effect.js → Загружается при изменении
onInput$() → chunk-input.js → Загружается при вводе
// ✅ Правильно — функция захватывает только сериализуемые данные
const name = useSignal('Qwik');
const onClick = $(() => {
console.log(name.value); // Signal — сериализуемый
});
// ❌ Неправильно — Promise не сериализуем
const promise = fetch('/api');
const onClick = $(() => {
promise.then(r => r.json()); // Ошибка оптимизатора!
});
// ✅ Правильно — создаём Promise внутри $
const onClick = $(() => {
fetch('/api').then(r => r.json()); // OK!
});
// ✅ Правильно
import { someUtil } from './utils';
const handler = $(() => {
someUtil(); // Статический импорт — OK
});
// ❌ Избегайте динамических импортов внутри $
const handler = $(() => {
import('./heavy-lib').then(lib => lib.doSomething()); // Проблема
});

Qwik поддерживает prefetching — предварительную загрузку чанков:

// vite.config.ts или qwik-city конфигурация
export default defineConfig({
plugins: [
qwikCity(),
qwikVite({
client: {
// Стратегия prefetch
prefetchStrategy: {
implementation: {
linkInsert: 'html-append', // Вставить <link rel="prefetch">
linkRel: 'prefetch', // Тип prefetch
}
}
}
})
]
});
Окно терминала
# Анализ бандла после сборки
npm run build
# Размеры бандлов в dist/build/
dist/
build/
q-manifest.json # Манифест всех чанков
q-abc123.js # Компонент (0.5 КБ)
q-def456.js # Обработчик onClick (0.2 КБ)
q-ghi789.js # Эффект (0.3 КБ)

Qwik использует Service Worker для кеширования и prefetch:

src/routes/service-worker.ts
import { setupServiceWorker } from '@builder.io/qwik-city/service-worker';
setupServiceWorker();
// Qwik автоматически предзагружает вероятные следующие взаимодействия
// на основе паттернов использования

Для браузерных объектов, которые не нужно сериализовать:

import { noSerialize, useStore } from '@builder.io/qwik';
const store = useStore({
// Регулярные данные сериализуются
items: [] as string[],
count: 0,
// noSerialize — только клиентский объект
audioContext: noSerialize<AudioContext | undefined>(undefined),
});