跳到内容

使用 HTTP/2 和 WebLink 预加载资源和资源提示

编辑此页

Symfony 通过 WebLink 组件提供原生支持来管理 Link HTTP 标头,当使用 HTTP/2 和现代 Web 浏览器的预加载功能时,这是提高应用程序性能的关键。

Link 标头用于 HTTP/2 服务器推送 和 W3C 的 资源提示,以便在客户端知道它们需要资源(例如 CSS 和 JavaScript 文件)之前将资源推送给客户端。WebLink 还支持其他与 HTTP 1.x 协同工作的优化。

  • 要求浏览器在后台获取或渲染另一个网页;
  • 进行早期的 DNS 查询、TCP 握手或 TLS 协商。

需要考虑的重要事项是,即使在本地机器上工作,所有这些 HTTP/2 功能都需要安全的 HTTPS 连接。主要的 Web 服务器(Apache、nginx、Caddy 等)都支持这一点,但您也可以使用由 Symfony 社区的 Kévin Dunglas 创建的 Symfony 的 Docker 安装程序和运行时

安装

在使用 Symfony Flex 的应用程序中,在开始使用 WebLink 功能之前,请运行以下命令来安装它:

1
$ composer require symfony/web-link

预加载资源

假设您的应用程序包含如下网页:

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,您的应用程序可以在浏览器请求 CSS 文件内容之前将其发送给浏览器。

为了实现这一点,请更新您的模板以使用 WebLink 提供的 preload() Twig 函数。请注意,"as" 属性是必需的,因为浏览器使用它来正确地优先处理资源并遵守内容安全策略。

1
2
3
4
5
6
7
<head>
    <!-- ... -->
    {# note that you must add two <link> tags per asset:
       one to link to it and the other one to tell the browser to preload it #}
    <link rel="preload" href="{{ preload('/app.css', {as: 'style'}) }}" as="style">
    <link rel="stylesheet" href="/app.css">
</head>

如果您重新加载页面,则感知的性能将会提高,因为当浏览器仅请求 HTML 页面时,服务器同时响应了 HTML 页面和 CSS 文件。

提示

当使用 AssetMapper 组件 链接到资源(例如 importmap('app'))时,无需添加 <link rel="preload"> 标签。当 WebLink 组件可用时,importmap() Twig 函数会自动为您添加 Link HTTP 标头。

注意

您可以通过使用 preload() 函数包装资源来预加载它:

1
2
3
4
5
<head>
    <!-- ... -->
    <link rel="preload" href="{{ preload(asset('build/app.css')) }}" as="style">
    <!-- ... -->
</head>

此外,根据 优先级提示规范,您可以使用 importance 属性来指示要下载的资源的优先级:

1
2
3
4
5
<head>
    <!-- ... -->
    <link rel="preload" href="{{ preload('/app.css', {as: 'style', importance: 'low'}) }}" as="style">
    <!-- ... -->
</head>

它是如何工作的?

WebLink 组件管理添加到响应中的 Link HTTP 标头。当在前面的示例中使用 preload() 函数时,以下标头被添加到响应中:Link </app.css>; rel="preload"; as="style"。根据 预加载规范,当 HTTP/2 服务器检测到原始 (HTTP 1.x) 响应包含此 HTTP 标头时,它将自动触发在同一 HTTP/2 连接中推送相关文件。

流行的代理服务和 CDN,包括 CloudflareFastlyAkamai 也利用了此功能。这意味着您可以立即将资源推送到客户端并提高生产环境中应用程序的性能。

如果您想阻止推送,但让浏览器通过发出早期的单独 HTTP 请求来预加载资源,请使用 nopush 选项:

1
2
3
4
5
<head>
    <!-- ... -->
    <link rel="preload" href="{{ preload('/app.css', {as: 'style', nopush: true}) }}" as="style">
    <!-- ... -->
</head>

资源提示

资源提示 由应用程序使用,以帮助浏览器决定应首先下载、预处理或连接哪些资源。

WebLink 组件提供了以下 Twig 函数来发送这些提示:

  • dns_prefetch():“指示将用于获取所需资源的源(例如 https://foo.cloudfront.net),并且用户代理应尽早解析”。
  • preconnect():“指示将用于获取所需资源的源(例如 http://127.0.0.1)。启动早期连接(包括 DNS 查询、TCP 握手和可选的 TLS 协商)允许用户代理掩盖建立连接的高延迟成本”。
  • prefetch():“识别下一个导航可能需要的资源,并且用户代理应该获取该资源,以便用户代理在将来请求该资源时可以提供更快的响应”。
  • prerender():“识别下一个导航可能需要的资源,并且用户代理应该获取并执行该资源,以便用户代理稍后在请求该资源时可以提供更快的响应”。

该组件还支持发送与性能无关的 HTTP 链接和任何实现 PSR-13 标准的链接。例如,HTML 规范中定义的任何链接

1
2
3
4
5
6
<head>
    <!-- ... -->
    <link rel="alternate" href="{{ link('/index.jsonld', 'alternate') }}">
    <link rel="preload" href="{{ preload('/app.css', {as: 'style', nopush: true}) }}" as="style">
    <!-- ... -->
</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
    {
        // using the addLink() shortcut provided by AbstractController
        $this->addLink($request, (new Link('preload', '/app.css'))->withAttribute('as', 'style'));

        // alternative if you don't want to use the addLink() shortcut
        $linkProvider = $request->attributes->get('_links', new GenericLinkProvider());
        $request->attributes->set('_links', $linkProvider->withLink(
            (new Link('preload', '/app.css'))->withAttribute('as', 'style')
        ));

        return $this->render('...');
    }
}
本作品,包括代码示例,在 Creative Commons BY-SA 3.0 许可下获得许可。
目录
    版本