互联网发展到如今,数据的重要性已经不须要再多的强调,那如何作好数据搜集的工做则是每一家公司都要面临的问题。html
数据搜集能够有不一样的选择。有的公司选择使用第三方统计的SDK,好比友盟、神策等;有的公司选择本身在产品中注入统计代码,搭建查询系统,固然后者的代价会比较大,但优势就是更贴近公司的业务。前端
代码埋点vue
代码埋点就是在须要数据统计的地方植入数据上报的代码,统计用户行为。git
优势:能够很是精确的选择何时发送数据。 缺点:维护代价较大,每一次更新都要对埋点代码进行维护,不然大几率搜集不到旧版本的数据。github
可视化埋点后端
使用可视化交互手段代替写代码,把核心代码和配置、资源分开,每次打开都经过网络更新配置和资源。跨域
优势:解决埋点代价大和维护代价大的问题。 缺点:覆盖的功能有限,不是全部的控件均可以经过这种方案定制。浏览器
无埋点服务器
也就是全埋点的意思,无埋点尽量收集全部控件的操做数据,而后再在系统里进行数据分析。cookie
优势:对页面全部元素进行埋点,能够获取页面元素点击几率,并进一步分析。 缺点:数据传输和服务器压力相对较大。
依然是先从一个思惟导图开始。
自执行方法
确保埋点工具能够即插即用,只要加载完毕就能够自动上报部分数据。具体实现方式以下:
(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,有时须要统计页面停留的时长。基于这些基础需求,整理了以下须要搜集的数据:
客户端信息对于前端开发来讲属于相对比较头疼的问题,各类魔改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
会话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来维护时间线,更多关于维护时间线的问题能够看下面的连接。
数据存储与读取
采用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>
复制代码
开发一个前端数据埋点工具,不须要特别复杂的技术,更可能是基于业务的思考。这里把其中比较关键的部分列举出来,做为一个参考:
完整代码与使用方法,请移步GitHub,感谢。