Предварительная загрузка ресурсов и подсказок источников с помощью HTTP/2 и WebLink
Дата обновления перевода 2024-08-01
Предварительная загрузка ресурсов и подсказок источников с помощью 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('...');
}
}