如要可靠地追蹤涵蓋使用者造訪網頁整個過程 (不只是載入網頁前) 的數據分析、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()。
建議的備用策略是使用下方的 polyfill,該 polyfill 會在內部使用 fetch() 搭配 keepalive 或 navigator.sendBeacon() 處理不支援的瀏覽器。您的程式碼必須直接呼叫 fetchLater(),切勿自行呼叫 fetch()、sendBeacon() 或其他信標 API。
fetchLater() polyfill
請使用下列最小 fetchLater() polyfill,在不支援的瀏覽器中盡可能實作 API。
這個 polyfill 唯一顯著的行為差異,在於它使用 visibilitychange 偵測使用者何時離開,而不是依賴瀏覽器的原生卸載處理。這是內部實作詳細資料,您的程式碼不需要監聽 visibilitychange 或任何其他頁面生命週期事件。只要呼叫 fetchLater(),polyfill 就會處理傳送作業。
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;
},
};
};