Symfony4 の HTTP/2 Preload
本記事の実行環境&対象バージョン
- nginx 1.14.0
- PHP 7.3.10
- Symfony 4.3.5
HTTP/2 Preload
HTTP/2 Preloadは 「必要なリソースを先に読み込んでブラウザキャッシュしておく」 という機能です。
CSSや画像、スクリプトなど、実際に適用するタイミングで読み込んでおいたリソースを使うことができるので、 Webアプリのパフォーマンスが良くなる可能性があります。
Preloadを利用する場合、HTMLのlinkタグでrel="preload"を指定するか、 HTTPレスポンスヘッダにLinkを指定します。
なおHTTP/2 Preloadの詳細は下記サイトをご覧ください。
- W3CのPreloadドラフト日本語訳
- nginxのHTTP/2 Server Push紹介ブログ
- 「Preload を用いたリソースプリローディングの最適化」
- DeNA Techcon (2016/01/29) 登壇資料 by Kazuho Oku
SymfonyでPreload
Symfonyでは WebLinkコンポーネント を利用することで、簡単にLinkヘッダを追加できます。
まずSymfony WebLinkコンポーネントをインストールします。
php composer.phar require symfony/web-link
インストールが完了したらtwigファイルを編集します。 Preloadしたいリソースをpreload関数で書くのがポイントです。
スタイルシートのPreload
<link rel="stylesheet" href="{{ preload(asset('ramune.css'), {as: 'style', nopush: true}) }}">
<link rel="stylesheet" href="{{ preload(asset('code.css'), {as: 'style', nopush: true}) }}">
オプションのnopush: trueはPreloadでリソースを取得することを指し、外すとリソースがサーバからプッシュされるようになります(HTTP/2 Push)。
Preloadはブラウザがリソースを取りに行くのに対し、 Pushはサーバーがリソースを送りつける動きになります。 nginxのブログ にある図(Three configurations were tested to measure the impact of HTTP/2 with server push)がわかりやすいので、 ぜひご覧ください。
jsのプリロードも同様にpreload関数を使います。
jsのPreload
<script src="preload(asset('build/ramune.js'), {as: 'script', nopush: true})"></script>
jsファイルのプリロードだけを行いたい場合、素直にlinkタグを書いた方が良いです。 もしHTTP/2 Pushを行いたい場合はHTTPヘッダのLinkが必要なので、 Controllerで制御する方法を検討してください。
scripタグを書かずにPreloadだけしたい場合
{# 素直にrel="preload"で書く場合 #}
{# HTTPレスポンスヘッダのLinkは乗らないので、Pushは不可能 #}
<link rel="preload" as="script" href="{{ asset('ramune.js') }}">
{# 無理やりpreload関数を使う場合(Linkヘッダに乗る) #}
{% set js = preload(asset('build/ramune.js'), {as: 'script', nopush: true}) %}
WebpackEncoreBundleを利用している場合、webpackのビルド結果ファイルが複数だったりハッシュ値が付いている場合があります。 都度ファイル名を調べてハードコードするのは大変なので、encore_entry_css_filesを使って対象エントリーのファイル名配列を取得します。
WebpackEncoreBundleを利用している場合
{% for css in encore_entry_css_files('ramune') %}
<link rel="stylesheet" href="{{ preload(asset(css), {as: 'style', nopush: true}) }}">
{% endfor %}
{# jsはencore_entry_js_files関数が用意されている #}
{% for js in encore_entry_js_files('ramune') %}
...
{% endfor %}
WebLinkコンポーネントのPreload確認
HTTPレスポンスヘッダにlinkが付与されていればOKです。