背景
互联网发展到现在,数据的重要性已经不需要再多的强调,那如何做好数据搜集的工作则是每一家公司都要面临的问题。
数据搜集可以有不同的选择。有的公司选择使用第三方统计的SDK,比如友盟、神策等;有的公司选择自己在产品中注入统计代码,搭建查询系统,当然后者的代价会比较大,但优点就是更贴近公司的业务。
数据埋点技术
代码埋点
代码埋点就是在需要数据统计的地方植入数据上报的代码,统计用户行为。
优点:可以非常精确的选择什么时候发送数据。缺点:维护代价较大,每一次更新都要对埋点代码进行维护,否则大概率搜集不到旧版本的数据。
可视化埋点
使用可视化交互手段代替写代码,把核心代码和配置、资源分开,每次打开都通过网络更新配置和资源。
优点:解决埋点代价大和维护代价大的问题。缺点:覆盖的功能有限,不是所有的控件都可以通过这种方案定制。
无埋点
也就是全埋点的意思,无埋点尽可能收集所有控件的操作数据,然后再在系统里进行数据分析。
优点:对页面所有元素进行埋点,可以获取页面元素点击概率,并进一步分析。缺点:数据传输和服务器压力相对较大。
构建思路与核心代码
依然是先从一个思维导图开始。
自执行方法
确保埋点工具可以即插即用,只要加载完毕就可以自动上报部分数据。具体实现方式如下:
(function (win, doc) {
var BP = {
// 开放接口代码
};
win.BP = BP;
})(window, document);
这样在js文件加载完毕时,就可以直接在全局使用BP来调用埋点工具的方法了。
埋点方式
使用代码埋点的方式来上报数据,在工具中定义:
var BP = {
send: function () {
// 发送数据方法
}
};
在页面的关键操作方法中通过BP.send()调用。
同时,考虑到服务端渲染的情况,页面可能直接由后端输出。后端开发者也可以直接在标签中添加属性bp-data,来实现用户有交互操作时进行数据上报。
/**
* 埋点,捕获带有bp-data属性的节点点击事件
*/
var buryingPoint = function () {
var attr = 'bp-data';
var evtType = utils.mobile ? 'touchstart' : 'mousedown';
utils.addEvent(doc, evtType, function (evt) {
var target = evt.srcElement || evt.target;
while (target && target.parentNode) {
if (target.hasAttribute(attr)) {
BP.send();
break;
}
target = target.parentNode;
}
});
};
为了兼顾PC与移动端浏览器,将utils.addEvent设计为一个可以跨浏览器侦听事件的方法,具体实现方法如下:
var utils = {
/**
* 跨浏览器事件侦听
*/
addEvent: function () {
if (doc.attachEvent) {
return function (ele, type, func) {
ele.attachEvent('on' + type, func);
};
} else if (doc.addEventListener) {
return function (ele, type, func) {
ele.addEventListener(type, func, false);
};
}
}()
}
数据搜集
抛开业务来讲,通常需要统计的数据往往是uv和pv,有时需要统计页面停留的时长。基于这些基础需求,整理了如下需要搜集的数据:
客户端信息对于前端开发来说属于相对比较头疼的问题引用js文件加上时间戳,各种魔改UserAgent严重影响开发者们的情绪。相信各大公司对于UserAgent判断也有一个较为成熟的处理,作为个人开发来说推荐一个代码库ua-device,可以减少很多这方面的工作。唯一的不足,是引用这个库,会使打包出来的js文件体积增加150KB左右,个人认为在当前网络环境下这点无需顾虑。
// 浏览器信息
var CI = {
size: function () {
return scr.width + 'x' + scr.height;
}(),
// 网络类型
network: function () {
return (nav.connection && nav.connection.type) ? nav.connection.type : '-';
}(),
// 语言
language: function () {
return nav.language || '';
}(),
timezone: function () {
return new Date().getTimezoneOffset() / 60 || '';
}(),
ua: function () {
return encodeURIComponent(ua);
}(),
os: function () {
var o = uaOutput.os;
return encodeURIComponent(o.name + '_' + o.version.original);
}(),
browser: function () {
var b = uaOutput.browser;
return b.name + '_' + b.version.original;
}(),
engine: function () {
var e = uaOutput.engine;
return e.name + '_' + e.version.original;
}()
};
会话id用于计算uv,在工具初始化的时候生成一个uuid。由于会话id在打开页面后不会更新,所以使用类vue计算属性的方式来实现。
var BP = {
/**
* 会话id,刷新页面会更新
*/
sessionId: function () {
return UUID.create();
}()
}
设备id用于串联用户的行为。比如用户浏览了若干个页面,上报了数条数据,就可以用设备id将这些行为串联起来。由于前端无法真正获取到所用设备的唯一标识,所以与会话id一样,采用不会重复的uuid。同样也是类vue的计算属性。
BP = {
/**
* 设备id,读取cookie,不存在则种入cookie
*/
deviceId: function () {
var did = utils.getCookie(cookieName);
if (!did) {
did = UUID.create();
utils.setCookie(cookieName, did, year);
}
return did;
}()
}
记录页面停留时长成本最低的方法就是使用轮询上报数据,请求间隔可以根据业务需求来定。毕竟间隔越小,服务器承载的压力就会更大一点,但获取的数据就更准确。
/**
* Ticker钩子函数,用于上报页面停留时长
* @param dt 间隔时间
*/
var calStayTime = function (dt) {
totalTime += dt;
if(totalTime >= stayTime) {
BP.send();
totalTime -= stayTime;
}
};
// 启动ticker
ticker.start();
ticker.register(calStayTime);
// 页面离开时不再计时
utils.addEvent(doc, 'visibilitychange', function () {
if (doc.visibilityState === 'hidden') {
ticker.stop();
} else {
ticker.start();
}
});
为了能够上报尽可能准确的停留时间,当离开页面时(比如最小化或切换标签)应当停止计时。这里用一个独立的,简易版本的Ticker来维护时间线,更多关于维护时间线的问题可以看下面的链接。
使用TypeScript实现一个Ticker
数据存储与读取
采用cookie存储一些需要持久保存的数据,比如设备id。
var utils = {
/**
* 设置cookie
* @param name 名称
* @param value 值
* @param days 保存时间
* @param domain 域
*/
setCookie: function (name, value, days, domain) {
if (value === null) {
return;
}
if (domain === undefined || domain === null) {
// 去除host中的端口部分
domain = utils.stringSplice(win.location.host, '', ':', '');
}
if (days === undefined || days === null || days === '') {
doc.cookie = name + '=' + value + ';domain=' + domain + ';path=/';
} else {
var now = new Date();
var time = now.getTime() + DAY * days;
now.setTime(time);
doc.cookie = name + '=' + value + ';domain=' + domain + ';expires=' + now.toUTCString() + ';path/';
}
},
/**
* 读取cookie
* @param name 名称
*/
getCookie: function (name) {
if (name === undefined || name === null) {
return;
}
var reg = RegExp(name);
if (reg.test(doc.cookie)) {
return utils.stringSplice(doc.cookie, name, ';', '');
}
}
}
上报数据
埋点数据上报本质上可以看作是一种单向请求,即不需要关心服务器反馈,可以采用image标签的方式向服务器发送数据,同时还可以避免额外的跨域问题。
var utils = {
/**
* 发送请求,使用image标签跨域
* @param url 接口地址
*/
sendRequest: function (url) {
if (page.length === 0) {
console.error('请配置有效的page参数', '@burying-point');
return;
}
var img = new Image();
img.src = url;
}
}
扩展
添加一个白名单过滤,规定只有白名单内的域名才可以发送请求。这只是一个小把戏,避免在开发过程中上报过多的脏数据,增加数据分析的工作量。
var utils = {
/**
* 白名单校验
*/
checkWhiteList: function () {
if (whiteList.length === 0) {
return true;
}
var href = win.location.href;
var flag = false;
for (var i = 0; i < whiteList.length; i++) {
if (href.indexOf(whiteList[i]) > -1) {
flag = true;
break;
}
}
return flag;
}
}
使用方式
构建的埋点工具可以通过两种不同的方式进行数据上报。
第一种,通过代码直接上报:
<script>
BP.send();
script>
第二种,在DOM标签中添加bp-data属性:
<div bp-data>点击我会上报一条数据div>
总结
开发一个前端数据埋点工具,不需要特别复杂的技术引用js文件加上时间戳,更多是基于业务的思考。这里把其中比较关键的部分列举出来,作为一个参考:
完整代码与使用方法,请移步GitHub,感谢。
*声明:本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。
公众号ID:tzbc666
有趣的灵魂在等你
长按扫码可关注
点个好看和转发也是一种支持哟!
版权声明
本文仅代表作者观点。
本文系作者授权发表,未经许可,不得转载。
发表评论