最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript前端埋點(diǎn)神器navigator.sendBeacon詳細(xì)指南

 更新時(shí)間:2026年02月11日 10:26:35   作者:發(fā)現(xiàn)一只大呆瓜  
前端埋點(diǎn)是指在前端頁(yè)面中通過(guò)代碼手段,記錄用戶行為和頁(yè)面性能數(shù)據(jù)的技術(shù),下面這篇文章主要介紹了JavaScript前端埋點(diǎn)神器navigator.sendBeacon的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

在前端開發(fā)中,埋點(diǎn)系統(tǒng)是必不可少的一環(huán)。我們經(jīng)常需要在用戶關(guān)閉頁(yè)面、刷新跳轉(zhuǎn)路由時(shí),向服務(wù)器發(fā)送最后一條統(tǒng)計(jì)數(shù)據(jù)(比如用戶停留時(shí)長(zhǎng)、頁(yè)面跳出率)。

但這看似簡(jiǎn)單的需求,在實(shí)現(xiàn)時(shí)卻危機(jī)四伏:請(qǐng)求發(fā)不出去?頁(yè)面跳轉(zhuǎn)卡頓?今天我們就來(lái)聊聊這個(gè)問(wèn)題的終極解決方案 —— navigator.sendBeacon。

一、 痛點(diǎn)與傳統(tǒng)方案的掙扎

場(chǎng)景還原

當(dāng)用戶點(diǎn)擊關(guān)閉按鈕時(shí),瀏覽器會(huì)觸發(fā)生命周期事件(unloadvisibilitychange)。如果我們直接使用普通的異步 AJAX (xhrfetch) 發(fā)送請(qǐng)求,瀏覽器通常會(huì)忽略它,因?yàn)轫?yè)面都要銷毀了,瀏覽器不想處理未完成的請(qǐng)求。

傳統(tǒng)方案:同步 XHR

為了保證數(shù)據(jù)能發(fā)出去,以前的做法是將請(qǐng)求改為同步(Synchronous) 。

const syncReport = (url, { data = {}, headers = {} } = {}) => {
  const xhr = new XMLHttpRequest();
  // 第三個(gè)參數(shù) false 表示同步請(qǐng)求
  xhr.open('POST', url, false);
  xhr.withCredentials = true;
  Object.keys(headers).forEach((key) => {
    xhr.setRequestHeader(key, headers[key]);
  });
  xhr.send(JSON.stringify(data));
};

致命缺陷

  1. 用戶體驗(yàn)極差:同步請(qǐng)求會(huì)阻塞主線程。這意味著只有請(qǐng)求發(fā)送完成,頁(yè)面才能關(guān)閉或跳轉(zhuǎn)。在弱網(wǎng)環(huán)境下,用戶會(huì)感覺頁(yè)面“卡死”了。
  2. 瀏覽器廢棄:現(xiàn)代瀏覽器(如 Chrome)已經(jīng)明確表示將在頁(yè)面卸載期間禁用同步 XHR,這種方法遲早失效。

二、 救世主:navigator.sendBeacon

1. 概念

navigator.sendBeacon() 是專門為“頁(yè)面卸載時(shí)發(fā)送數(shù)據(jù)”而設(shè)計(jì)的 Web API。 它的核心能力是:將數(shù)據(jù)放入瀏覽器的發(fā)送隊(duì)列,即使頁(yè)面已經(jīng)關(guān)閉,瀏覽器也會(huì)在后臺(tái)默默完成發(fā)送。

2. 核心優(yōu)勢(shì)

  • 可靠性高:不受頁(yè)面生命周期影響,確保數(shù)據(jù)不丟失。
  • 非阻塞:完全異步執(zhí)行,不會(huì)阻塞頁(yè)面關(guān)閉或跳轉(zhuǎn),用戶體驗(yàn)絲滑。
  • 低優(yōu)先級(jí):瀏覽器會(huì)擇機(jī)發(fā)送(通常是網(wǎng)絡(luò)空閑時(shí)),不爭(zhēng)搶關(guān)鍵資源。

3. API 語(yǔ)法

const result = navigator.sendBeacon(url, data);
  • url:請(qǐng)求地址。

  • data:要發(fā)送的數(shù)據(jù),支持 ArrayBuffer、ArrayBufferView、Blob、DOMString、FormDataURLSearchParams。

  • result(返回值):布爾值 (true / false)。

    • true:表示數(shù)據(jù)成功加入傳輸隊(duì)列(注意:不代表服務(wù)器接收成功)。
    • false:表示隊(duì)列已滿,無(wú)法加入。

三、 實(shí)戰(zhàn):三種常見發(fā)送姿勢(shì)

1. 發(fā)送普通字符串

默認(rèn) Content-Typetext/plain。

const reportData = (url, data) => {
  // data 可能會(huì)被轉(zhuǎn)為字符串 "[object Object]",建議先 stringify
  navigator.sendBeacon(url, JSON.stringify(data));
};

2. 發(fā)送 JSON 數(shù)據(jù)(推薦)

如果你希望后端接收到的 Content-Typeapplication/json 或者 application/x-www-form-urlencoded,需要使用 Blob 來(lái)手動(dòng)指定。

const reportData = (url, data) => {
  // ? 正確寫法:Blob 的第二個(gè)參數(shù)才是 options
  const blob = new Blob([JSON.stringify(data)], {
    type: 'application/json; charset=UTF-8' // 或者 application/x-www-form-urlencoded
  });
  navigator.sendBeacon(url, blob);
};

3. 發(fā)送 FormData

適用于需要上傳文件或模擬表單提交的場(chǎng)景。瀏覽器會(huì)自動(dòng)設(shè)置 Content-Typemultipart/form-data。

const reportData = (url, data) => {
  const formData = new FormData();
  Object.keys(data).forEach((key) => {
    let value = data[key];
    // FormData 的 value 只能是字符串或 Blob
    if (typeof value !== 'string' && !(value instanceof Blob)) {
      value = JSON.stringify(value);
    }
    formData.append(key, value);
  });
  navigator.sendBeacon(url, formData);
};

四、跨域場(chǎng)景的“萬(wàn)能鑰匙” —— 1px 像素圖片

在某些場(chǎng)景下使用sendBeacon 會(huì)有跨域問(wèn)題,而使用1px像素圖片這種方式則利用了瀏覽器允許跨域加載資源(如圖片、腳本)的特性,繞過(guò)了復(fù)雜的 CORS 配置

1. 核心原理

通過(guò)動(dòng)態(tài)創(chuàng)建 Image 對(duì)象,將埋點(diǎn)數(shù)據(jù)通過(guò) URL Query 的形式掛載在圖片請(qǐng)求的地址后面。服務(wù)端在接收到請(qǐng)求后,記錄日志并返回一個(gè) 1x1 像素的透明圖片。

2. 代碼實(shí)現(xiàn)

/**
 * 跨域埋點(diǎn)發(fā)送:1px 像素圖片方案
 * @param {string} url - 接口地址
 * @param {Object} data - 埋點(diǎn)數(shù)據(jù)
 */
const reportByImg = (url, data) => {
  // 1. 構(gòu)造查詢參數(shù)字符串
  const params = Object.keys(data)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
    .join('&');

  // 2. 創(chuàng)建圖片實(shí)例
  const img = new Image();

  // 3. 監(jiān)聽回調(diào)(可選,用于監(jiān)控發(fā)送是否成功)
  img.onload = () => console.log('埋點(diǎn)發(fā)送成功');
  img.onerror = (err) => console.error('埋點(diǎn)發(fā)送失敗', err);

  // 4. 添加時(shí)間戳用于確保每次請(qǐng)求都被視為獨(dú)立的新資源,避免被一些代理服務(wù)器或?yàn)g覽器機(jī)制意外緩存
  const connector = url.includes('?') ? '&' : '?';
  img.src = `${url}${connector}${params}&_t=${Date.now()}`;
};

3. 方案對(duì)比:為什么不用其他方式?

特性1px ImageAjax (XHR/Fetch)sendBeacon
跨域限制無(wú)(天然支持)嚴(yán)格(需服務(wù)端配置 CORS)較松(但仍受限于安全策略)
頁(yè)面卸載可能被截?cái)?/td>極易被截?cái)?/td>可靠(異步非阻塞)
兼容性極好(所有瀏覽器)一般較好(現(xiàn)代瀏覽器)
承載數(shù)據(jù)量?。ㄊ?URL 長(zhǎng)度限制)中(通常 < 64KB)

4.注意事項(xiàng)

  • URL 長(zhǎng)度限制:由于數(shù)據(jù)是帶在 URL 上的,瀏覽器對(duì) URL 長(zhǎng)度有限制(通常為 2KB-8KB)。如果數(shù)據(jù)量巨大,請(qǐng)拆分發(fā)送或改用 sendBeacon。
  • GIF 是首選:服務(wù)端推薦返回 GIF 格式。對(duì)比 PNG 和 JPG,GIF 的透明像素塊在文件頭開銷上是最小的(僅 43 字節(jié))。
  • 內(nèi)存釋放:在一些極端高頻埋點(diǎn)場(chǎng)景下,建議在 onload 之后執(zhí)行 img = null 徹底釋放內(nèi)存。

五、 避坑指南(面試考點(diǎn))

  1. 請(qǐng)求類型固定sendBeacon 只能發(fā)送 POST 請(qǐng)求。
  2. 無(wú)法讀取響應(yīng):這是一個(gè)“射后不理”的 API,你無(wú)法獲取服務(wù)器返回的數(shù)據(jù)(狀態(tài)碼、Response Body 等)。
  3. 數(shù)據(jù)大小限制:雖然標(biāo)準(zhǔn)沒有明確規(guī)定,但瀏覽器對(duì)隊(duì)列總大小有限制(通常在 64KB 左右),不適合發(fā)送大數(shù)據(jù)。
  4. Cookie 攜帶sendBeacon 默認(rèn)會(huì)攜帶同域的 Cookie。

六、 面試模擬題

Q1:sendBeacon和ajax(XHR/Fetch) 有什么根本區(qū)別?

參考回答:

  • 生命周期:Ajax 請(qǐng)求屬于頁(yè)面上下文,頁(yè)面關(guān)閉時(shí)請(qǐng)求會(huì)被取消(除非同步);sendBeacon 屬于瀏覽器上下文,頁(yè)面關(guān)閉后依然存活。
  • 交互體驗(yàn):頁(yè)面卸載時(shí),同步 Ajax 會(huì)阻塞跳轉(zhuǎn);sendBeacon 是異步非阻塞的。
  • 功能限制sendBeacon 只能 POST,無(wú)法自定義 headers(除了 Content-Type),且無(wú)法讀取響應(yīng)。

Q2:如果瀏覽器不支持sendBeacon怎么辦?

參考回答: 需要做降級(jí)處理。

  1. 檢測(cè) navigator.sendBeacon 是否存在。
  2. 如果不存在,降級(jí)為 同步 XHR 請(qǐng)求(雖然體驗(yàn)差,但得保數(shù)據(jù))。
  3. 或者使用 <img> 標(biāo)簽發(fā)送 GET 請(qǐng)求(僅限數(shù)據(jù)量極小且不需要響應(yīng)的場(chǎng)景)。

Q3:sendBeacon返回true代表數(shù)據(jù)一定發(fā)送成功了嗎?

參考回答: 不一定。返回 true 僅代表瀏覽器成功將數(shù)據(jù)加入了發(fā)送隊(duì)列。如果網(wǎng)絡(luò)斷開、或者瀏覽器崩潰,數(shù)據(jù)依然可能發(fā)送失敗。但相比于普通 Ajax,它的成功率已經(jīng)高出了幾個(gè)數(shù)量級(jí)。

到此這篇關(guān)于JavaScript前端埋點(diǎn)神器navigator.sendBeacon詳細(xì)指南的文章就介紹到這了,更多相關(guān)JS前端埋點(diǎn)navigator.sendBeacon內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

中阳县| 太保市| 洮南市| 三原县| 宽甸| 周至县| 多伦县| 无极县| 兴海县| 富阳市| 远安县| 金山区| 女性| 东源县| 苏尼特右旗| 宜城市| 安新县| 广元市| 建德市| 晋宁县| 敖汉旗| 桦南县| 镇巴县| 久治县| 出国| 光泽县| 乐安县| 垫江县| 博客| 游戏| 岢岚县| 正定县| 珠海市| 临沂市| 宿松县| 冷水江市| 织金县| 富源县| 昌江| 冷水江市| 德江县|