相关文章推荐
谦虚好学的太阳  ·  Nat. Biotechnol. | ...·  1 年前    · 
彷徨的热水瓶  ·  Prerender vue.js ...·  2 年前    · 
喝醉的米饭  ·  C++ Map ...·  2 年前    · 

1. 直接发送 xhr 请求
我们会优先想到 监听 页面的 unload 或者 beforeunload 事件,在事件 回调 里使用 XMLHttpRequest 发送异步请求。

但是由于是 xhr 请求是异步发送,很可能在它即将发送的时候,页面已经卸载了,从而导致发送取消或者发送失败。异步请求响应返回后,由于页面和相关资源已经卸载,会引起 function not found 的错误。

解决方法就是 AJAX 通信改成同步发送,即只有发送完成,页面才能卸载。

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));
 

xhr请求改为同步,虽然能够完成发送数据,但存在以下两个问题:

  1. 部分浏览器已经不支持同步的 XMLHttpRequest 对象了(即open()方法的第三个参数为false);
  2. xhr请求改为同步后,会阻塞页面的卸载和跳转,导致下一个页面导航加载的时机变晚,用户体验较差。

2. 动态图片
通过在unload事件处理器中,创建一个图片元素并设置它的 src 属性的方法来延迟卸载以保证数据的发送。因为绝大多数浏览器会延迟卸载以保证图片的载入,所以数据可以在卸载事件中发送。

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('&')}`;
 

这种方法存在同样的问题,页面卸载流程被阻塞,后面页面的加载时机被延迟,用户体验不好

Navigator.sendBeacon

浏览器引入的sendBeacon方法,**发出的是异步请求,但是请求是作为浏览器任务执行的,与当前页面是脱钩的。**因此该方法不会阻塞页面卸载流程和延迟后面页面的加载。
1. 基本用法

navigator.sendBeacon(url, data);

url 就是上报地址,data 可以是 ArrayBufferViewBlobDOMStringFormdata,根据官方规范,需要 request header 为 CORS-safelisted-request-header,在这里则需要保证 Content-Type 为以下三种之一:

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

我们一般会用到 DOMString , BlobFormdata 这三种对象作为数据发送到后端,下面以这三种方式为例进行说明。

// 1. DOMString类型,该请求会自动设置请求头的 Content-Type 为 text/plain
const reportData = (url, data) => {
  navigator.sendBeacon(url, data);
// 2. 如果用 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);
// 3. 发送的是Formdata类型,
// 此时该请求会自动设置请求头的 Content-Type 为 multipart/form-data。
var data = {
   name: '前端名狮子'  ,
   age: 20
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);
 

sendBeacon 如果成功进入浏览器的发送队列后,会返回true;如果受到队列总数、数据大小的限制后,会返回false。返回ture后,只是表示进入了发送队列,浏览器会尽力保证发送成功,但是否成功了,无法判断。

2. 发送数据大小限制
目前没有给出具体的发送数据大小限制标准,不过有人做了下面的测试,当数据长度是65536时,异步请求进入浏览器发送队列失败,表明数据大小是有限制,不同的浏览器应该有所差别。:

var url = 'http://jsfiddle.net?sendbeacon';
var n = 65536; // sendBeacon limit for Chrome v40 on Windows (2^16)
var data = new Array(n+1).join('X'); // generate string of length n
if(!navigator.sendBeacon(url, data))
   alert('data limit reached');
 

3. 兼容性
sendBeacon方法存在兼容性问题,除了IE,大部分浏览器都已经支持,兼容情况如下图所示:

考虑到兼容性问题,使用过程中,我们可以采用下面两种方法做兼容:

  1. 不支持时,用同步的xhr替代,也就是上面提到的第一种方法。
navigator.sendBeacon || new Function('var xhr=new XMLHttpRequest();xhr.open("POST",arguments[0],true);r.send(arguments[1]);');
 
// npm 包
npm install navigator.sendbeacon
// 外链
<script src="https://unpkg.com/navigator.sendbeacon"></script>
<!-- OR -->
<script src="https://cdn.jsdelivr.net/npm/navigator.sendbeacon"></script>
 

sendBeacon方法具有如下特点:

  1. 发出的是异步请求,并且是POST请求,后端解析参数时,需要注意处理方式;
  2. 发出的请求,是放到的浏览器任务队列执行的,脱离了当前页面,所以不会阻塞当前页面的卸载和后面页面的加载过程,用户体验较好;
  3. 只能判断出是否放入浏览器任务队列,不能判断是否发送成功;
  4. Beacon API不提供相应的回调,因此后端返回最好省略response body

转载于:https://blog.csdn.net/u012193330/article/details/102778979

sendBeacon发出的是异步请求,并且是POST请求,后端解析参数时,需要注意处理方式;发出的请求,是放到的浏览器任务队列执行的,脱离了当前页面,所以不会阻塞当前页面的卸载和后面页面的加载过程,用户体验较好;只能判断出是否放入浏览器任务队列,不能判断是否发送成功;Beacon API不提供相应的回调,因此后端返回最好省略。
之所以介绍这个还在草案中的方法,是源于最近新做的一个活动。该活动有个需求,就是用户离开该页面的某个时间段之后,发个请求给该用户送券。后来是通过setTimeout来做的,用户离开该页面,该页面进入后台。这时在页面中设置的定时器依然是有效的,但是不同手机对这个有效时间的兼容性不一样,有长有短。safari浏览器则是暂停的。 navigator.sendBeacon()方法可以用来从用户代理向服务...
默认是发post请求,后端需要能够post接收数据 Beacon API不提供相应的回调,因此后端返回最好省略response body,(例如,204 No Content) navigator.sendBeacon()之后的代码可以执行 ... var data = { ajax_data: 22 }; // 只能 添加 type 字段,无法添加其它内容 var blob = new Blob([JSON.stringify(data)], {type : 'application/json'}); // Content-Type application/json navigator.sendBeacon('ajax.ph
navigator.sendBeacon()方法可用于通过HTTP POST将少量数据异步传输到 Web 服务器。 它主要用于将统计数据发送到 Web 服务器,同时避免了用传统技术(如:XMLHttpRequest)发送分析数据的一些问题。 navigator.sendBeacon(url); navigator.sendBeacon(url, data); Copy to Clipboard url参数表明data将要被发送到的网络地址。 data可选......
有一个任务非常耗时会消耗后台大量算力,所以在退出页面的时候,要求前端这边发送一个请求来杀死任务。 一开始以为这个需求非常简单,就是在进入其他路由前,发送一下请求,杀死一下任务就好了。 然而现实狠狠的打了我的脸,因为退出页面的场景不止切换路由~ 退出页面场景: 还在本网站,跳到其他路由 刷新页面/关闭页面也需要发送请求来杀死任务 还在本网站,跳到其他路由 这个比较简单,在Vue中可以通过...
使用Navigator.sendBeacon(url,data) 发送页面关闭时的接口请求 在项目中遇见需要在页面关闭时发送请求以获得用户关闭时间计算出用户浏览时长 首先是使用 window.$.ajax 方法正常发送请求,但是async:false的情况下接口报错 NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load xxxx(具体原因可百度),将async设置为true则时而无法请求成功,这是因为如果请求是异
记录一下折磨了两天的uniapp热更新报错安装失败[-1201]:WGT/WGTU文件格式错误 1、报错原因是因为下载路径/下载文件错了,我的服务器上映射的下载地址访问后打开一个网页(图1),需要进去后点击下载 实际上需要的是一个下载链接(图2)这里的地址 2、下载文件错误是生成的wgt文件不对,需要重新生成:Hbuilder——发行——原生App-制作应用wgt包 window.addEventListener("pagehide", function() {} 2.使用navigator.sendBeacon,浏览器引入了Navigator.sendBeacon()方法。这个方法还是异步发出请求,但是请求与当前页面脱钩,作为浏览器的任务,因此可以保证会把数据发出去,不拖延卸载流程。 // 测试接口 let parames = {
我没了解过数据分析师这个职业,也不知道他们需要用哪些数据做什么。早之前做过页面统计,用的是神策和百度统计,里面那些数据个人觉得已经很全面了,最近公司进行一些特殊埋点统计,需要在市场想要的地方一个一个加方法调接口。 这不是重点,重点是请求发起之后,如果直接跳转,请求会被浏览器取消。如果要自己统计页面浏览时长和点击事件,就只能做一些延迟和牺牲一些准确性,反正就是页面跳转需要统计的数据会丢失一些。 早之前做过,还是看的阮一峰双循环延迟阻塞页面卸载,除了死循环延迟,还有其他旁门左道,比如ajax设置同步,然后在
前端能发起HTTP请求的方式: 1、ajax(Asynchronous JavaScript + XML)技术:ajax的核心是XMLHttpRequest,通过对该对象的操作来进行异步的数据请求。有同源限制。 接触的最多的就是jQuery的封装,比如$.ajax   $.post   $.get