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

15. SSR и SSG

Qwik City поддерживает несколько режимов рендеринга: Server-Side Rendering (SSR), Static Site Generation (SSG) и их комбинации. Выбор режима влияет на производительность, SEO и масштабируемость.

SSR (Server-Side Rendering)
↳ HTML генерируется на сервере при каждом запросе
↳ Данные всегда актуальны
↳ Подходит: новостные сайты, личные кабинеты
SSG (Static Site Generation)
↳ HTML генерируется при сборке
↳ Максимальная скорость (отдаётся как файл)
↳ Подходит: блоги, документация, лендинги
ISR (Incremental Static Regeneration)
↳ Статика с периодическим обновлением
↳ Баланс между скоростью и актуальностью
Hybrid
↳ Разные маршруты — разные режимы
↳ / → SSG, /blog → SSR, /dashboard → SSR

В Qwik City все страницы рендерятся на сервере по умолчанию:

src/routes/news/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
// Этот загрузчик выполняется на сервере при каждом запросе
export const useNews = routeLoader$(async () => {
const articles = await fetchLatestNews();
return articles;
});
export default component$(() => {
const news = useNews();
return (
<ul>
{news.value.map(article => (
<li key={article.id}>{article.title}</li>
))}
</ul>
);
});

Для статической генерации используется onStaticGenerate:

src/routes/blog/[slug]/index.tsx
import type { StaticGenerateHandler } from '@builder.io/qwik-city';
// Указываем какие slug'и нужно сгенерировать
export const onStaticGenerate: StaticGenerateHandler = async () => {
const posts = await getAllPosts();
return {
params: posts.map(post => ({ slug: post.slug })),
};
};
// Загрузчик данных работает и при SSG
export const usePost = routeLoader$(async ({ params }) => {
return await getPost(params.slug);
});

Режим рендеринга зависит от адаптера:

Окно терминала
# Статический хостинг (SSG)
npm run qwik add static
# Vercel Edge (SSR)
npm run qwik add vercel-edge
# Cloudflare Workers (SSR)
npm run qwik add cloudflare-pages
# Node.js (SSR)
npm run qwik add express
// SSR с кешированием
export const onGet: RequestHandler = ({ cacheControl }) => {
// Кешируем на 1 час, stale 24 часа
cacheControl({
maxAge: 3600,
staleWhileRevalidate: 86400,
public: true,
});
};
export const usePageData = routeLoader$(async ({ url, headers, cookie, env }) => {
// url — текущий URL
const page = Number(url.searchParams.get('page') ?? '1');
// headers — заголовки запроса
const userAgent = headers.get('user-agent');
// cookie — куки
const userId = cookie.get('userId')?.value;
// env — переменные окружения
const apiKey = env.get('API_KEY');
return { page, userId };
});

Qwik поддерживает потоковую передачу HTML:

// Qwik автоматически стримит HTML
// Браузер начинает отображать контент до полной загрузки
// Компоненты с отложенными данными рендерятся async
export default component$(() => {
const data = useLazyData(); // Отложенный загрузчик
return (
<div>
<div>Это отображается сразу</div>
<Suspense fallback={<div>Загрузка...</div>}>
{/* Это появится когда data загрузится */}
<DataComponent data={data} />
</Suspense>
</div>
);
});