Use sendBeacon for front-end data reporting

preface

Recently, I received a request to count the relevant data of the page and report it. This paper introduces some methods of data reporting.

Timing of reporting data

  • Page loading

At this time, the data can be reported only when the page is load ed.

 

window.addEventListener('load', reportData, false);
  • When a page is unloaded or refreshed

At this time, the data can be reported only when the page is before unload.

 

window.addEventListener('beforeunload', reportData, false);
  • During SPA route switching

    • If Vue router or react-router@3 And below, you can report in hooks.
    • If it's react-router@4 You need to report within the life cycle of the Routes root component.
  • When multiple tab s of a page are switched

If this is the case, you can read the document Visibilitystate or document Hidden distinguishes the activation status of the page tab and determines whether it needs to be reported.

 

document.addEventListener("visibilitychange", function() {
  if(document.visibilityState === 'visible') {
    reportData();
  }
  if(document.visibilityState === 'hidden') {
    reportData2();
  }
  // your code ...
});

Method of reporting data

1. Directly send request and report

We can send data directly to the back end through ajax, taking axios as an example.

 

axios.post(url, data);

However, there is a problem with this method, that is, if the report is made when the page is unloaded or refreshed, the request may be cancel led by the browser before it is sent to the server before the browser is closed or reloaded, resulting in data reporting failure.

We can change the ajax request to the synchronization method, which can ensure that the request can be sent to the server. Since neither fetch nor axios supports synchronization requests, you need to send synchronization requests through XMLHttpRequest.

 

const syncReport = (url, { data = {}, headers = {} } = {}) => {
  const xhr = new XMLHttpRequest();
  xhr.open('POST', url, false);
  xhr.withCredentials = true;
  Object.keys(headers).forEach((key) => {
    xhr.setRequestHeader(key, headers[key]);
  });
  xhr.send(JSON.stringify(data));
};

It should be noted here that changing the request to synchronization will block the process of page closing or reloading, which will affect the user experience.

2. Dynamic pictures

We can delay the unloading by creating a picture element in the beforeunload event handler and setting its src attribute to ensure the sending of data. Because most browsers delay the unloading to ensure the loading of pictures, the data can be sent in the unloading event.

 

const reportData = (url, data) => {
  let img = document.createElement('img');
  const params = [];
  Object.keys(data).forEach((key) => {
    params.push(`${key}=${encodeURIComponent(data[key])}`);
  });
  img.onload = () => img = null;
  img.src = `${url}?${params.join('&')}`;
};

At this time, the server can return a 1px * 1px picture to ensure that the onload event of img is triggered. However, if some browsers cannot ensure the loading of pictures in implementation, the reported data will be lost.

3. sendBeacon

In order to solve the above problems, we have navigator.sendBeacon Method, using this method to send the request can ensure the effective delivery of data without blocking the unloading or loading of the page, and the coding is simpler than the above method.

The usage is as follows:

 

navigator.sendBeacon(url, data);

url is the report address. The data can be ArrayBufferView, Blob, DOMString or Formdata. According to the official specification, the request header needs to be CORS-safelisted-request-header Here, you need to ensure that the content type is one of the following three types:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

We generally use domstring, blob and Formdata to send data to the back end. We will take these three methods as examples.

  • DOMString

If the data type is string, it can be reported directly. At this time, the content type of the request header will be automatically set to text/plain.

 

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

If Blob is used to send data, we need to manually set the MIME type of Blob, which is generally set to 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

You can directly create a new Formdata. At this time, the content type of the request header will be automatically set to 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 can only append string or Blob
      value = JSON.stringify(value);
    }
    formData.append(key, value);
  });
  navigator.sendBeacon(url, formData);
};

Notice the JSON Stringify operation, the server needs to parse the data to get the correct data.

summary

We can use sendBeacon to send data. This method can not only ensure the reliability of data, but also does not affect the user experience. If the browser does not support this method, it can be degraded to send data using synchronized ajax.



Author: z4d
Link: https://www.jianshu.com/p/04e88271a8f2
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Added by florida_guy99 on Thu, 10 Feb 2022 10:23:55 +0200