Next.js 15で103 EarlyHintsを使う
Early Hintsについて
EarlyHintsを利用するとブラウザがHTMLを受け取る前に、必要なリソースのダウンロードを行うことができます。
例えばdevtoolでHTML読み込みを観察したときに「サーバーの応答を待機しています」が長い場合、ブラウザはただ待機するしかなくなります。
ですが、EarlyHintsでの読み込みを行うと、サーバーの応答を待機しながらも、必要なリソース読み込みを同時に行えます。以下フロー図の比較です。
実装方法について
概要
cloudflareのEarlyHintsサポートを利用します。
これは、Linkヘッダーを付与することで、指定したリソースのEarlyHintsを可能にします。
ref:
実際の実装
page.tsx
検証のために、下記の読み込みを比較します。
<script async={true} src="https://imamiya-masaki.github.io/thirdparty-js/deliver/third-party2.js"/>
<script async={true} src="https://imamiya-masaki.github.io/thirdparty-js/deliver/third-party.js"/>
~/third-party.js
,~/third-party2.js
に関しては、下記のコードを参照しています。
内容は同じもので名称だけ異なります。
また、今回Next.js 15 RC
を利用しているため、React 19 RC
のscriptタグの機能拡張を利用しています。こちらは、async={true}
とすることで、headタグ内に、scriptタグを差し込みます。
下記のコードで意図的にサーバーの応答待機時間を伸ばします。
function sleep(milliseconds: number) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
await sleep(6000)
next.config.mjs
Next.jsでは、config.jsで、pathごとのHTTPヘッダーを設定できます。
third-party.jsとheavy-image.jpgに対して、Linkヘッダーを付与します。
動きについて
EarlyHintsが有効になっているかどうかは、curl
コマンドでも確認できます。
実際に、103ステータスが返ってきていることを確認できます。
次に、ページに訪問してみます。
third-party.js
とthird-party2.js
は名称以外全て同じで、キャッシュの設定なども異なりませんが、third-party.js
の方は、ディスクキャッシュから取得され、ダウンロード時間が短くなっていることがわかります。
Streamingとの対比
Next.jsではEarlyHintsと似た恩恵を受けられる機能として、Streamingがあります。
こちらは、レンダリングが完了した部分のHTMLを素早く返し、
順次、ブラウザに返せる状態になったものからレスポンスします。
ブラウザからは長時間ダウンロードされているように見えます。
サーバーの応答待機時間に該当するような処理は、基本的にはbody内部の生成になります。
そのため、headタグだけであれば短時間で返すことができるため、Streaming + rel=preload
を利用すると、EarlyHintsに似た恩恵を受けることができます。
Streaming
メリット
- わざわざ特定のリソースを指定しなくても、サーバーの応答待機時間を有効活用できる
- EarlyHintsが対応していないCDNでも利用可能
デメリット
- App Routerでしか利用できない。(そのため Next.js 13以上)
- エラーステータスを返せない
EarlyHints
メリット
- EarlyHintsが利用可能な環境であれば、簡単に導入可能
デメリット
- 利用する場合は、リソースを指定しないといけない。
私見
私の意見としては、Streamingの技術はEarlyHintsの恩恵以外も受けることができるため、
可能であればStreamingによる早期リソース取得の方が、より良いと考えています。
とはいえ、AppRouter自体がまだ普及しきれていないため、
サーバーの応答待機時間がボトルネックなサービスで、簡単にStreamingの導入が難しそうであればとりあえずEarlyHintsの有効化を行うのは有効な手段かと思います。
Discussion