Реалізація Resource Hints (preload, prefetch, preconnect, dns-prefetch)
Resource Hints — директиви браузеру: почни робити цю роботу прямо сейчас, не чекай, поки парсер до неї дійде. Різниця між правильно розставленими підказками та їх відсутністю — 300–800 мс на реальному мобільному пристрої. Неправильно розставлені підказки можуть навредити: займуть полосу пропускання потрібними запитами або витіснять критичні ресурси з кешу.
Чотири директиви та їх сенс
dns-prefetch — заранее резолвит DNS для домену. Коштує копійки по ресурсам, дає 20–120 мс економії при першому запиті до домену.
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="dns-prefetch" href="//fonts.googleapis.com">
<link rel="dns-prefetch" href="//www.google-analytics.com">
preconnect — резолвит DNS + встановлює TCP-соединение + TLS-hendshejk. Економія 100–500 мс. Дорожче за dns-prefetch, тому тільки для ресурсів, які точно знадобляться на сторінці.
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
preload — завантажує ресурс з високим приоритетом до того, як браузер виявив його в DOM. Критично для LCP-зображення, основного шрифту, критичного CSS.
<link rel="preload" href="/hero-image.webp" as="image" fetchpriority="high">
<link rel="preload" href="/fonts/Inter-Regular.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/main.js" as="script">
prefetch — завантажує ресурс з низьким приоритетом для використання на наступній сторінці. Браузер робить це тільки в простоя.
<link rel="prefetch" href="/checkout">
<link rel="prefetch" href="/assets/checkout-chunk.js" as="script">
Що та коли використовувати
| Директива | Коли | Домен | Ресурс |
|---|---|---|---|
dns-prefetch |
Сторонні домени, не 100% потрібні | Будь-який сторонній | — |
preconnect |
Сторонні домени, точно потрібні | Будь-який сторонній | — |
preload |
Критичні ресурси поточної сторінки | Будь-який | LCP-img, шрифти, critical CSS |
prefetch |
Ресурси наступної сторінки | Будь-який | JS-чанки, HTML, дані |
Реалізація в Laravel
// app/Http/Middleware/ResourceHints.php
class ResourceHints
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$hints = [
'<https://fonts.googleapis.com>; rel=preconnect',
'<https://fonts.gstatic.com>; rel=preconnect; crossorigin',
'</fonts/Inter-Regular.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin',
];
if ($request->routeIs('catalog.*')) {
$hints[] = '</assets/product-page-chunk.js>; rel=prefetch; as=script';
}
$response->headers->set('Link', implode(', ', $hints));
return $response;
}
}
HTTP-заголовок Link працює так само, як тег <link> в HTML, але браузер отримує його раніше — до початку парсингу HTML.
Реалізація в React/Next.js
У Next.js використовуємо компонент <Head>:
import Head from 'next/head';
export default function ProductPage({ product }) {
return (
<>
<Head>
<link rel="preconnect" href="https://cdn.example.com" />
<link rel="dns-prefetch" href="//analytics.example.com" />
{product.heroImage && (
<link
rel="preload"
href={product.heroImage}
as="image"
fetchpriority="high"
/>
)}
</Head>
{/* ... */}
</>
);
}
Preload для Google Fonts без FOUT
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap">
</noscript>
Або хостимо шрифти самостійно:
<link rel="preload" href="/fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/inter-600.woff2" as="font" type="font/woff2" crossorigin>
Розумний prefetch на основі поведінки користувача
Prefetch при наведенні з задержкою:
const prefetchCache = new Set();
document.addEventListener('mouseover', (e) => {
const link = e.target.closest('a[href]');
if (!link || prefetchCache.has(link.href)) return;
if (link.origin !== location.origin) return;
const timeout = setTimeout(() => {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = link.href;
document.head.appendChild(prefetch);
prefetchCache.add(link.href);
}, 100);
link.addEventListener('mouseleave', () => clearTimeout(timeout), { once: true });
});
Бібліотека quicklink робить це систематично:
import quicklink from 'quicklink';
quicklink.listen({
ignores: [
/\/logout/,
/\/admin/,
(url) => url.includes('?'),
],
});
Частих помилки
- Preload без використання — ресурс передзавантажений, але не використаний на сторінці. Preload-ресурс повинен використовуватися на цій же сторінці.
- Занадто багато preconnect — більше 6 preconnect-директив починають мешкати основним запитам.
-
Preload шрифтів без
crossorigin— шрифт завантажиться двічі. -
dns-prefetchтаpreconnectдля одного домену — це надлишково.
Терміни
Аудит поточних resource hints та внедрення базового набору — 4–8 годин. Реалізація динамічних підказок через HTTP-заголовки + розумний prefetch — 1–3 робочих дні.







