首頁 > 軟體

Navigator sendBeacon頁面關閉也能傳送請求方法範例

2022-06-30 18:01:22

背景

最近在需求中有一個這樣的場景:

需要在頁面關閉的時候,使用者不需要操作,主動關閉當前訂單

當時考慮的方案:在頁面關閉的時候,向後端傳送一個請求,將這個資源釋放掉;

定下方案時,覺得也不是什麼難事,覺得谷歌瀏覽器應該會提供頁面關閉的 API 供開發者使用。

經過查詢,找到了這麼兩個 API :beforeunload 和 unload

beforeunload

當瀏覽器視窗關閉或者重新整理時,會觸發 beforeunload 事件。當前頁面不會直接關閉,可以點選確定按鈕關閉或重新整理,也可以取消關閉或重新整理。

window.addEventListener('beforeunload', function (event) {
  // Cancel the event as stated by the standard.
  event.preventDefault();
  // Chrome requires returnValue to be set.
  event.returnValue = '';
});

該事件會使網頁在離開或者重新整理的時候彈出一個對話方塊,給使用者一個提示。在這個彈框出現時,該頁面是做不了任何操作的,除非把這個彈框關閉。其他頁面也只能進行簡單的點選瀏覽操作,鍵盤是操作不了的。

這個不符合使用者無感知的條件

unload

當檔案或一個子資源正在被解除安裝時, 觸發 unload 事件。

unload 事件在 beforeunload 事件後觸發,這時候檔案處於一個什麼狀態呢?
所有資源都存在,像圖片,iframe的等,但是這些資源對於使用者來說均不可見,介面上的互動也是無效的.

使用方式和 beforeunload 相同,但是 unload 事件中不能使用確認框,畢竟都已經在解除安裝了

window.addEventListener('unload', function(event) {
  console.log('unload');
});

問題

  • 使用者無論重新整理還是關閉了頁面,因為這兩種操作都會呼叫beforeunload和 unload。
  • 非同步請求會被 cancel 掉,導致請求無法傳送成功,由於使用的axios進行請求,瀏覽器有機率關閉非同步請求,造成請求無法發出,可以嘗試換成同步。
  • 在事件的回撥中使用同步的 AJAX 請求。
window.addEventListener('unload', function (event) {
  let xhr = new XMLHttpRequest();
  xhr.open('post', '/log', false);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.send('foo=bar');
});

但是谷歌瀏覽器已經不允許頁面關閉期間進行同步的 XMLHTTPRequest(),這條規則適用於 beforeunload、unload、pagehide和visibilitychange這些 API;

為了確保頁面在解除安裝時講資料傳送到伺服器,官方建議使用 sendBeacon()或者 Fetch keep-alive

Navigator.sendBeacon

官方連結 https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/sendBeacon

這個方法主要用於滿足統計和診斷程式碼的需要,這些程式碼通常嘗試在解除安裝(unload)檔案之前向web伺服器傳送資料。

Beacon API 有以下這樣幾個特點:

  • 通過 HTTP POST 將少量資料非同步傳輸,可靠性好
  • 這個請求不需要響應,保證在頁面的 unload 狀態從發起到完成之前被傳送。
  • 不會阻塞頁面解除安裝,也就不會影響下一導航的載入
  • 支援跨域
  • 不支援自定義請求頭

用法如下:

navigator.sendBeacon(url, data);

url 就是上報地址,data 可以是 ArrayBufferView,Blob,DOMString 或 Formdata,根據官方規範,需要 request header 為 CORS-safelisted-request-header,在這裡則需要保證 Content-Type 為以下三種之一:

application/x-www-form-urlencoded

multipart/form-data

text/plain

我們一般會用到 DOMString , Blob 和 Formdata 這三種物件作為資料傳送到後端,下面以這三種方式為例進行說明。

DOMString

如果資料型別是 string,則可以直接上報,此時該請求會自動設定請求頭的 Content-Type 為 text/plain。

const reportData = (url, data) => {
  navigator.sendBeacon(url, data);
};

Blob

如果用 Blob 傳送資料,這時需要我們手動設定 Blob 的 MIME type,一般設定為 application/x-www-form-urlencoded。

const reportData = (url, data) => {
  const blob = new Blob([JSON.stringify(data), {
    type: 'application/x-www-form-urlencoded',
  }]);
  navigator.sendBeacon(url, blob);
};

Formdata

可以直接建立一個新的 Formdata,此時該請求會自動設定請求頭的 Content-Type 為 multipart/form-data。

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

注意這裡的 JSON.stringify 操作,伺服器端需要將資料進行 parse 才能得到正確的資料。

Fetch keep-alive

當使用fetch() 方法時,如果把keeplive 設定為true,即便頁面被終止請求也會保持連線。

window.onunload = function() {
  fetch('/analytics', {
    method: 'POST',
    body: "statistics",
    keepalive: true
  });
};

以上就是Navigator sendBeacon頁面關閉也能傳送請求方法範例的詳細內容,更多關於Navigator sendBeacon請求傳送的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com