ユーザーがウェブページにアクセスしている間(ページ読み込み時だけでなく)の分析データ、Core Web Vitals、テレメトリー データを確実にトラッキングするには、fetchLater() API を使用します。
fetchLater() API は、レスポンスが不要で、配信タイミングが緊急でない場合に、サーバーにデータを送信する最も信頼性の高い方法です。これは、ユーザー分析、テレメトリー、エラー トラッキング、Core Web Vitals などのパフォーマンス指標などのデータに適用されます。
unload イベント リスナーで <img> ピクセルを作成するなどの古い手法は、信頼性が低いことで知られており(特にモバイルの場合)、パフォーマンスに悪影響を及ぼす可能性があります(ページが bfcache の対象外になるため)。
導入方法
リクエストをスケジュールする: 関連するデータが利用可能になったらすぐに、データ ペイロードを指定して
fetchLater()を呼び出します。これにより、データがキューに格納され、後で送信されます。必要に応じてペイロードを更新する: ユーザーがさらにデータを生成した場合や、状態が変化した場合は、以前にスケジュールしたリクエストを中止し、完全に更新されたスナップショットを使用して
fetchLater()を再度呼び出します。残りの処理はブラウザに任せる: ユーザーがページから移動したり、タブを閉じたりすると、ブラウザは最新の
fetchLater()呼び出しからペイロードを確実に送信します。
コード例
このコードは、fetchLater() を使用して、更新されたセッション継続時間とともに 10 秒ごとに新しいビーコンをキューに追加することで、ユーザーのページ訪問のセッション継続時間を測定します。
const ANALYTICS_ENDPOINT = '/path/to/analytics/endpoint';
const sessionData = {
duration: 0,
id: crypto.randomUUID(),
};
let fetchLaterController = null;
function queueBeacon() {
// Abort any pending beacons before creating a new one.
if (fetchLaterController) {
fetchLaterController.abort();
}
fetchLaterController = new AbortController();
// Update session duration to the current page time.
sessionData.duration = performance.now();
// Schedule a fetch for the data payload to be sent later.
// IMPORTANT: wrap the call in a try/catch to handle quota errors.
try {
fetchLater(ANALYTICS_ENDPOINT, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: JSON.stringify(sessionData),
signal: fetchLaterController.signal,
});
} catch (error) {
// Handle errors as needed.
}
}
// Update the session data and queue a new beacon every 10 seconds.
setInterval(queueBeacon, 10000);
ベスト プラクティス
- 推奨: レスポンスが不要で、データをすぐに送信する必要がない場合は、
fetchLater()を使用してサーバーにデータを送信します。 - ユーザーがページを離れる前にデータを更新する必要がある場合は、
AbortControllerを使用して保留中のフェッチをキャンセルする - 推奨: 割り当て(現在はオリジンあたり約 64 KB)を超えないように、ペイロード サイズを最小限に抑えます。
fetchLater()の呼び出しをtry/catchでラップして、割り当てエラーを処理する。globalThisでfetchLater()の存在を検出する機能を実装し、API をサポートしていないブラウザ用のフォールバック戦略を実装します。- リクエスト本文に
ReadableStreamオブジェクトを使用しないでください。エラーが発生します。
ブラウザのサポートとフォールバック戦略
fetchLater() がベースライン目標を達成していない場合は、フォールバック戦略が必要です。ただし、この API の信頼性とパフォーマンスの向上を考慮すると、ブラウザがサポートしている場合は fetchLater() を使用する必要があります。
推奨されるフォールバック戦略は、以下のポリフィルを使用することです。このポリフィルは、keepalive または navigator.sendBeacon() で fetch() を使用して、サポートされていないブラウザを内部的に処理します。独自のコードで fetchLater() を直接呼び出す必要があります。fetch()、sendBeacon()、またはその他のビーコン API を自分で呼び出さないでください。
fetchLater() ポリフィル
次の最小限の fetchLater() ポリフィルを使用します。これは、サポートされていないブラウザで API を可能な限り忠実に実装します。
このポリフィルで注目すべき動作の違いは、ブラウザのネイティブなアンロード処理に依存するのではなく、visibilitychange を使用してユーザーが離脱したことを検出することです。これは内部実装の詳細であり、コードで visibilitychange やその他のページ ライフサイクル イベントをリッスンする必要はありません。fetchLater() を呼び出すだけで、ポリフィルが配信を処理します。
globalThis.fetchLater ??= function fetchLater(url, init = {}) {
let timeoutHandle;
let activated = false;
function sendNow() {
if (!(init.signal && init.signal.aborted)) {
// Use fetch keepalive if the browser supports it or if custom fetch
// parameters are specified (e.g. custom headers or methods).
// Otherwise fall back to `navigator.sendBeacon()`.
if (
'keepalive' in Request.prototype ||
init.method !== 'POST' ||
init.headers
) {
fetch(url, Object.assign({}, init, {keepalive: true}));
activated = true;
} else {
activated = navigator.sendBeacon(url, init.body);
}
}
destroy();
}
function destroy() {
document.removeEventListener('visibilitychange', sendNow);
clearTimeout(timeoutHandle);
}
if (document.visibilityState === 'hidden') {
// If the beacon was created while the page is already hidden, send data
// ASAP but wait until the next microtask to allow all sync code to run.
queueMicrotask(sendNow);
} else {
document.addEventListener('visibilitychange', sendNow);
if (typeof init.activateAfter === 'number' && init.activateAfter >= 0) {
timeoutHandle = setTimeout(sendNow, init.activateAfter);
}
}
if (init.signal) {
init.signal.addEventListener('abort', destroy);
}
return {
get activated() {
return activated;
},
};
};