网页追踪用户行为

2020/1/19 JavaScript统计监测

统计代码发送方案

# 网页追踪用户行为

公共方法

var config = {
    monitor_url: commonConfig.websitePrefix() + '/api/monitor/ut'
}
var tools = {
    jsonToParams:function(json, notNeed){
        var parameStr = notNeed ? '' : '?';
        for(var i in json){
            if(typeof json[i] == 'object'){
                parameStr += i + '=' + encodeURI(JSON.stringify(json[i])) + '&'; 
            }else{
                parameStr += i + '=' + encodeURI(json[i]) + '&'; 
            } 
        }
        parameStr = parameStr.substring(0,parameStr.length - 1);
        return parameStr;
    }
}
var _monitor = {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 同步请求

使用 XMLHttpRequest 发送同步请求的方式已经计划从规范中删除,不再建议开发者使用。

_monitor.blockSend = function(params){
    let xhr = new XMLHttpRequest();
    xhr.open('post', config.monitor_url, false);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send(tools.jsonToParams(params, true));
}
1
2
3
4
5
6

同步请求,需要等待请求返回,阻塞后续js的执行。无论在任何情况下,都不推荐使用,十分影响用户体验。

# 强制延时

_monitor.delayedSend = function(params){
    let xhr = new XMLHttpRequest();
    xhr.open('post', config.monitor_url, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send(tools.jsonToParams(params, true));
    
    for (let i = 1; i < 10000; i++) {
        for (let m = 1; m < 10000; m++) { continue; }
    }
}
1
2
3
4
5
6
7
8
9
10

异步请求,通过强制的循环来延长停留时间,没有阻塞,但是仍旧会有卡顿的感觉,不建议。

# 图片请求

_monitor.picSend = function(params) {
    var body = document.getElementsByTagName('body')[0];
    var monitor_pic = document.createElement('img');
    log_pic.src = config.monitor_url + _monitor.jsonToParams(params);
    body.appendChild(monitor_pic);
    body.removeChild(monitor_pic);
}
1
2
3
4
5
6
7

GET请求,通过访问插入图片的方式通讯,不会阻塞js,但是会有小概率丢失的情景,可以使用。

# Ftech

_monitor.ftechSend = function(params) {
    fetch(config.monitor_url, {
      method: 'POST',
      body: _monitor.jsonToParams(params, true),
      headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'token': 'token'
      },
      keepalive: true
    });
}
_canUseFetch() {
    if (navigator && navigator.userAgent) {
    // 检测浏览器版本 来决定是否开启 fetch
    var userAgent = navigator.userAgent || "";
    var appVersion = navigator.appVersion || "";
    var vendor = navigator.vendor || "";

    var ua = (
        userAgent +
        " " +
        appVersion +
        " " +
        vendor
    ).toLowerCase();

    var match =
        /(chrome)[ \/]([\w.]+)/.exec(ua) ||
        /(webkit)[ \/]([\w.]+)/.exec(ua) ||
        /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
        /(msie) ([\w.]+)/.exec(ua) ||
        /(trident)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
        (ua.indexOf("compatible") < 0 &&
        /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua)) ||
        [];

    var engine = match[0];
    var mainVersion = match[2].split(".")[0];

    // chrome 内核版本大于 46, firefox 版本大于39 才开启 fetch
    return engine.indexOf("chrome") === 0 && mainVersion >= 46 || engine.indexOf("mozilla") === 0 && mainVersion >= 39;
}
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
41
42

fetch的keepalive属性,可以保证页面无论是否关闭,请求都会持续到结束(限制64KB)。解决请求第三次握手前,浏览器关闭,导致的请求失败问题。

# Beacon

底层使用的还是fetch,所以限制都是64KB,优势是简单好用

_monitor.beaconSend = function(params) {
    navigator.sendBeacon(config.monitor_url, params);
}
1
2
3

POST请求,最新选择。相当于把js行为从页面上升到浏览器维度,关闭页面后传输队列中的请求也会发送。兼容性差一点,所以最好搞一个兼容写法。

# 总结

一个兼容写法,尽量减少数据丢失

_monitor.send = function(params) {
 var state = navigator.sendBeacon(config.monitor_url, params);
    if(!state){
     _monitor.picSend(params)
    }
}
1
2
3
4
5
6