上报方式

我们采集了埋点数据后, 就需要把采集的数据交给后端。
那么我们应该如何上报?
我们得考虑接口跨域、上报异常(正在进行上报时, 用户关闭了页面, 这样上报就会失败)、性能要求(不能应用应用使用)

基于以上要求, 提供了三种方式供用户自行选择

  • sendBeacon
    浏览器引入的 sendBeacon 方法,发出的是异步请求,但是请求是作为浏览器任务执行的,
    与当前页面是脱钩的。因此该方法不会阻塞页面卸载流程和延迟后面页面的加载,用户体验较好。
    缺点: 浏览器存在支持问题

  • img

    避免跨域
    1x1 像素 img 对网页内容的影响几乎没有影响
    图片请求不占用 Ajax 请求限额
    不会阻塞页面加载,影响用户的体验
    相比 XMLHttpRequest 对象发送 GET 请求,性能上更好
    触发 GET 请求之后不需要获取和处理数据、服务器也不需要发送数据

  • post
    超出 get 最大请求数、或者用户手动进行上报方式配置则会采用 post

默认情况下优先级 sendBeacon > img > post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 判断上传长度 2000 个字符
const urlIsLong = (url: string) => {
let totalLength = 0,
charCode = 0;
for (var i = 0; i < url.length; i++) {
charCode = url.charCodeAt(i);
if (charCode < 0x007f) {
totalLength++;
} else if (0x0080 <= charCode && charCode <= 0x07ff) {
totalLength += 2;
} else if (0x0800 <= charCode && charCode <= 0xffff) {
totalLength += 3;
}
}
return totalLength < 2000 ? false : true;
};

if (navigator.sendBeacon) {
sendBeacon(url, params);
} else if (method === "POST" || urlIsLong(str)) {
xmlRequest(url, params);
} else {
const img = new Image();
img.src = `${url}?${str}`;
}

上报拦截

用户可能需要在上传前对参数进行变更, 我们可以在上报前提供拦截器给用户进行变更参数操作。
这里借鉴Axios Interceptor拦截器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Interceptor {
private handlers;
constructor() {
this.handlers = [];
}

// params promise then resolve reject
use(fulfilled, rejected, options) {
this.handlers.push({
fulfilled,
rejected
});
}
...
}

export default Interceptor;

class SimpleJsTracker {
constructor(options: IDefaultOptions) {
this['_init'](options);
this['interceptors'] = {
sendTracker: new Interceptor()
}
}
...
}

SJTracker.prototype.sendTracker = function (data = {}) {
let reportData = Object.assign(Object.assign({}, this._options), data);
// 调用拦截方法
this.interceptors.sendTracker.handlers.forEach((interceptor) => {
const onFulfilled = interceptor.fulfilled;
reportData = onFulfilled(reportData);
});
sendTracker(this._options, reportData);
};



文中完整代码
github > npm