作者:myprelude@github
原文链接: https://myprelude.github.io
转载请注明出处,保留原文链接和作者信息。
作为前端开发,你有没有想过我们开发的产品每天有多少人访问;他们都在我们开发的页面做了什么不可告人的秘密;如何去追踪用户的浏览轨迹去排除故障以及给我们产品经理提供他们报表数据呢?
埋点原理
上图可以看出来用户通过浏览器访问我们的页面,在我们页面进行一系列的操作,我们前端通过js获取操作信息、用户设备等核心信息,然后上报到服务端,服务端将我们信息收集做相应的处理,在将服务器处理好的数据下发到我们后台页面生成可视化界面,让产品经理来分析近期站点表现情况。
解决什么问题
- 记录uv pv
- 记录用户点击事件
- 记录用户的浏览轨迹
- 记录用户在页面停留时间
- 记录页面打开时间和首屏打开时间
- 记录页面http反应时间和页面错误收集
为自己的前端站点进行埋点
我们需要的埋点信息整理
明确目标,我们先规划好需要埋点信息:
- 1)用户设备信息 js navigator document window
- 2)用户点击事件埋点
- 3)页面打开和加载时间 js performance
- 4)页面错误信息收集 Error
(function(){
const webInfo = {};
// 收集设备信息
if (document) {
webInfo.domain = document.domain || ''; //获取域名
webInfo.url = document.URL || ''; //当前Url地址
webInfo.title = document.title || '';
webInfo.referrer = document.referrer || ''; //上一跳路径
}
if (window && window.screen) {
webInfo.sh = window.screen.height || 0; //获取显示屏信息
webInfo.sw = window.screen.width || 0;
webInfo.cd = window.screen.colorDepth || 0;
}
if (navigator) {
webInfo.lang = navigator.language || ''; //获取所用语言种类
webInfo.appVersion = navigator.appVersion || ''; // 获取浏览器的版本
}
if (performance) {
// 页面加载完成的时间
webInfo.loadPage = t.loadEventEnd - t.navigationStart;
//【解析 DOM 树结构的时间
webInfo.domReady = t.domComplete - t.responseEnd;
//【内容加载完成的时间
webInfo.request = t.responseEnd - t.requestStart;
//【执行 onload 回调函数的时间
webInfo.loadEvent = t.loadEventEnd - t.loadEventStart;
// 卸载页面的时间
webInfo.unloadEvent = t.unloadEventEnd - t.unloadEventStart;
// TCP 建立连接完成握手的时间
webInfo.connect = t.connectEnd - t.connectStart;
}
webInfo.isPC = IsPC();
webInfo.typeMobile = typeMobile();
// 判断是PC还是移动
function IsPC() {
let userAgentInfo = navigator.userAgent;
let Agents = ["Android", "iPhone",
"SymbianOS", "Windows Phone",
"iPad", "iPod"];
let flag = true;
for (let v = 0; v < Agents.length; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}
// 判断移动端系统
function typeMobile(){
let u = navigator.userAgent,type='';
if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1) {
type = '安卓手机'
} else if (u.indexOf('iPhone') > -1) {
type = '苹果手机'
} else if (u.indexOf('Windows Phone') > -1) {
type = 'winphone手机'
}
}
// 收集页面错误信息
window.onerror = function(errorMessage, scriptURI, lineNumber,columnNumber,errorObj) {
let ErrorMessage = {
"错误信息:":errorMessage
"出错文件:":scriptURI
"出错行号:":lineNumber
"出错列号:":columnNumber
"错误详情:":errorObj
}
}
})()
将收集到的页面上报
OK!完成页面基本信息的收集;我们需要上报信息到服务端,一般我们上传一些信息到服务端都是使用ajax,但是ajax在上传时会有跨域问题,因为我们统计的要统计很多不同站点的埋点信息,显然ajax就无法完成;但是在浏览器中script的src、link的href以及image的src都可以跨域;因为script标签和link标签都有自己的意义(一个是加载js一个加载css)所有这里使用image的src更好。......
function args_build(webInfo){
//拼接参数串
let args = '';
for (let i in webInfo) {
// alert(i);
if (args != '') {
args += '&';
}
args += i + '=' + encodeURIComponent(webInfo[i]); //将所有获取到的信息进行拼接
}
return args;
}
function createImage(webInfo){
// 创建图片上传数据
const Image = new Image();
Image.src = xxx/git.gif?args_build(webInfo);
}
上传页面错误信息和用户点击事件
一般页面的错误信息或者点击事件都是用户操作后(或者页面加载后)异步产生的;对于这类埋点信息一般我们关心的数据是相应的点击位置或者页面错误的具体信息;因此我需要一个单独函数处理。/**
*${param:object info} 埋点的具体信息
**/
function handlerInfo(info){
// info {id:埋点的id,message:埋点信息,url:埋点的路径 ...}
createImage(info)
}
一般我们定义好埋点信息的文档,哪里需要我们的手动埋点我们就调用这个方法;对于接口报错我们在ajax的catch中埋点。
我们页面如何加载埋点的方法
一般我们加载一段js都是通过直接script引入;但是埋点信息只是我们附加功能;对于使用者来说他只需要埋点就行不需要关心埋点的具体细节;所有我们会把代码做成一个sdk;在页面最后直接填写代码;如果是使用的webpack等打包工具这段代码都可直接生成。(function () {
const ma = document.createElement('script');
ma.type = 'text/javascript';
ma.async = true;
ma.src = "http://xxx/ubtSDK.js";
let s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ma, s);
})();
这样可以做到代码无侵入同时不影响页面加载。
服务端处理收集的数据(node)
const http = require('http');
const url = require("url");
const querystring = require("querystring");
http.createServer(function(req,res){
//获取返回的url对象的query属性值
const arg = url.parse(req.url).query;
//将arg参数字符串反序列化为一个对象
const params = querystring.parse(arg);
=== >>储存到数据库
}).listen(8088);
了解市场上一些埋点方案
目前市场上流行的埋点方案有:
- 手动埋点
- 可视化埋点
- 无埋点
手动埋点:(就和上面介绍的类似)
- 优点:可控性高
- 缺点:需要大量的人力成本,需要维护好埋点文档
可视化埋点:
- 通过可视化的界面拖拽配置实现,这些活动控件元素都带有唯一标识。通过埋点配置后台,将元素与要采集事件关联起来,可以自动生成埋点代码嵌入到页面中。
- 优点:快速埋点,让运营人员埋点
- 缺点:对页面结构要求高(页面需要DOM唯一缺点的标识)
- 实现:TalkingData、诸葛IO
无埋点:
- 这里不是说不需要埋点,而是前端自动采集全部事件,上报埋点数据,由后端来过滤和计算出有用的数据。原理:通过js检测页面所有的button input from等标签的事件,上传标签的属性以及操作行为等等。
- 优点:简单、快捷
- 缺点:数据量过大、服务端压力过大需要筛选大量数据
- 实现:GrowingIO
总结
不管是什么埋点,其实原理都是将数据提交给服务端,至于提交的实现方式就是根据需求来。通过image方式上传;将埋点代码以SDK的方式做到代码无侵入,不影响正常流程。