Попереднє завантаження ресурсів та підказок джерел за допомогою HTTP/2 і WebLink
Дата оновлення перекладу 2024-06-10
Попереднє завантаження ресурсів та підказок джерел за допомогою HTTP/2 і WebLink
Symfony надає нативну підтримку (через компонент WebLink) для управління HTTP-заголовками
Link
, які є ключем до покращення продуктивності додатку при використанні HTTP/2 і можливостей
попереднього завантаження сучасних веб-браузерів.
Заголовки Link
використовуються у пуші серверу HTTP/2 і підказках джерел
W3C для відправки джерел (наприклад, файлів CSS і JavaScript) клієнтам до того, як
вони взагалі зрозуміють, що вони їм потрібні. WebLink також включає інші опимізації,
які працюють з HTTP 1.x:
- Попросити браузер вилучити або відобразити іншу веб-сторінку фоново;
- Провести ранні послідовні пошуки DNS, рукостисканні TCP або перемовини TLS.
Важливо памʼятати, що всі ці функції HTTP/2 вимагають безпечного підключення HTTPS, навіть при роботі на локальній машині. Основні веб-сервери (Apache, nginx, Caddy і т.д.) підтримують це, але ви також можете використати установник і рантайм Docker для Symfony, створений Кевіном Дангласом зі спільноти Symfony.
Попереднє завантаження ресурсів
Уявіть, що у вашому додатку є така веб-сторінка:
1 2 3 4 5 6 7 8 9 10 11 12 13
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Application</title>
<link rel="stylesheet" href="/app.css">
</head>
<body>
<main role="main" class="container">
<!-- ... -->
</main>
</body>
</html>
Дотримуючись традиційного робочого процесу HTTP, коли подається ця сторінка, браузери робитимуть один запит для HTML-сторінки, а другий - для повʼязаного файлу CSS. Однак, завдяки HTTP/2, ваш додаток може почати відправляти зміст СSS-файлу ще до того, як браузер його запитає.
Щоб зробити це, спочатку встановіть компонент WebLink:
1
$ composer require symfony/web-link
Тепер, оновіть шаблон, щоб використовувати функцію Twig preload()
, надану
WebLink. Атрибут "as" є обовʼязковим, так як браузерам він потрібний для
застосування правильної пріоритизації та політики безпеки змісту:
1 2 3 4
<head>
<!-- ... -->
<link rel="preload" href="{{ preload('/app.css', { as: 'style' }) }}">
</head>
Якщо ви перезавантажите сторінку, продуктивність покращиться, так як сервер відповів і HTML-сторінкою, і CSS-файлом, хоча браузер запитав лише HTML-сторінку.
Note
Ви можете попередньо завантажити ресурс, огорнувши його у функцію preload()
:
1 2 3 4
<head>
<!-- ... -->
<link rel="preload" href="{{ preload(asset('build/app.css')) }}">
</head>
Крім того, відповідно до специфікації Пріоритетних підказок, ви можете сигналізувати
про пріоритет для завантаження, використовуючи атрибут importance
:
1 2 3 4
<head>
<!-- ... -->
<link rel="preload" href="{{ preload('/app.css', { as: 'style', importance: 'low' }) }}">
</head>
Як це працює?
Компонент WebLink управляє HTTP-заголовками Link
, доданими до відповіді. Пи
використанні функції preload()
у попередньому прикладі, наступний заголовок
було додано до відповіді: Link </app.css>; rel="preload"; as="style"
. Відповідно
до специфікації Попереднього завантаження, коли HTTP/2 сервер виявляє, що початковий
запит (HTTP 1.x) містить цей HTTP-заголовок, він автоматично запускає відправку для
повʼязаного файлу у тому ж зʼєднанні HTTP/2.
Популярні проксі-сервери та CDN, включно з Cloudflare, Fastly і Akamai, також використовують цю функцію. Це означає, що ви можете відправляти джерела клієнтам та покращувати продуктивність ваших додатків у виробництві прямо зараз.
Якщо ви хочете запобігти відправці, але дозволити браузеру попередньо завантажити
джерело, випустивший ранній окремий HTTP-запит, використайте опцію nopush
:
1 2 3 4
<head>
<!-- ... -->
<link rel="preload" href="{{ preload('/app.css', { as: 'style', nopush: true }) }}">
</head>
Підказки джерел
Підказки джерел використовуються додатками, щоб допомогти браузерам вирішити, які джерела мають бути завантажені, попередньо оброблені або підʼєднані у першу чергу.
Компонент WebLink надає наступні функції Twig для відправки цих підказок:
dns_prefetch()
: "означає першоджерело (наприклад,https://foo.cloudfront.net
), яке буде використано для отримання необхідних джерел, які агент користувача має розвʼязати якомога раніше".preconnect()
: "означає першоджерело (наприклад,https://www.google-analytics.com
), яке буде використано для отримання необхідних джерел. Запуск раннього зʼєднання, який включає в себе послідовний пошук DNS, рукостискання TCP та необовʼязкові перемовини TLS, дозволяє агенту користувача маскувати великі приховані витрати установки підключення".prefetch()
: "означає джерело, яке може бути запитане наступною навігацією, і яке агент користувача повинен отримати, щоб агент користувача міг доставити швидшу відповідь, коли джерело буде запитане пізніше".prerender()
: "означає джерело, яке може бути запитане наступною навігацією, і яке агенту джерела варто отримати і виконати, щоб агент користувача міг доставити швидшу відповідь, коли джерело буде запитане пізніше".
Даний компонент також підтримує відправку HTTP-посилань, не повʼязаних з продуктивністю, і будь-яких посилань, що реалізують стандарт PSR-13. наприклад, будь-яке посилання, визначене у специфікації HTML:
1 2 3 4 5
<head>
<!-- ... -->
<link rel="alternate" href="{{ link('/index.jsonld', 'alternate') }}">
<link rel="preload" href="{{ preload('/app.css', { as: 'style', nopush: true }) }}">
</head>
Попередній уривок призведе до того, що цей HTTP-заголовок буде відправлено клієнту:
Link: </index.jsonld>; rel="alternate",</app.css>; rel="preload"; nopush
Ви також можете додавати посилання в HTTP-запит напряму з контролера та сервісів:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\WebLink\GenericLinkProvider;
use Symfony\Component\WebLink\Link;
class BlogController extends AbstractController
{
public function index(Request $request): Response
{
// використання скорочення addLink(), наданого AbstractController
$this->addLink($request, new Link('preload', '/app.css'));
// альтернатива, якщо ви не хочете використовувати скорочення addLink()
$linkProvider = $request->attributes->get('_links', new GenericLinkProvider());
$request->attributes->set('_links', $linkProvider->withLink(
(new Link('preload', '/app.css'))->withAttribute('as', 'style')
));
return $this->render('...');
}
}