互聯網發展到如今,數據的重要性已經不須要再多的強調,那如何作好數據蒐集的工做則是每一家公司都要面臨的問題。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,感謝。