背景
公司以前採用GrowingIo
埋點,都是在每個專案中引入其提供的Web JS SDK
,但是最近由於使用期限到了,加上一些其他層面的考慮,準備採用神策做資料埋點和資料分析,因此需要移除每個專案中引入的GrowingIo Web JS SDK
,然後引入神策提供的Web JS SDK
。
方案
如果還是採用在每個專案中獨立引入Web JS SDK
,首先需要梳理出有多個專案有埋點需求,然後安排通知每個業務組的前端同學在迭代開發內引入。有沒有覺得特別麻煩,明明是同一個Web JS SDK
卻需要多次引入,還涉及到跨組協作、任務分配、資源安排等問題。更重要的是,如果下次又更換採用另一個埋點工具,那重複的事情又要做一遍,吃力不討好。
因此,我們希望能夠在某個地方引入後,專案就不需要獨立引入了,這個方案有沒有想到跟閘道器服務、中介軟體、攔截器的思想特別像呢?我們是否能採用這類思想解決呢?答案是肯定的,通過在前端路由服務請求到的靜態html
指令碼中注入神策Web JS SDK
對於一些前後端分離專案(Vue、React
)可以在前端路由服務中操作靜態html
指令碼注入神策Web JS SDK
,但對於一些前後端未分離的專案(例如freemarker
專案)該怎麼處理呢?可以在webagent
閘道器做統一處理嗎?其實也是可行的,攔截器可以攔截響應頭Content-Type: text/html
的響應,操作html
注入神策Web JS SDK
;如果不是此響應型別,則不做處理。但在實際情況中,鑑於前後端未分離專案較少,我們還是採用第一種方案,獨立在專案中引入。
封裝神策Web JS SDK
神策有提供全埋點功能,專案中希望使用啟用該功能,因此需要再封裝,初始化SDK
,可以參考如下封裝:
;(function () {
/** 判斷環境 */
var hostname = window.location.hostname;
var serverUrl = 'https://xxx?project=default'; // 測試環境資料接收地址
if (hostname === 'www.cassmall.com' || hostname === 'h.cassmall.com') {
serverUrl = 'https://xxx?project=production'; // 生產環境資料接收地址
}
// 開啟全埋點,Web JS SDK 全埋點包括三種事件:Web 頁面瀏覽、Web 元素點選、Web 視區停留
function addShenCeScript() {
window.cassSensors = window['sensorsDataAnalytic201505'];
// 初始化 SDK
window.cassSensors.init({
server_url: serverUrl, // 資料接收地址
is_track_single_page: true, // 單頁面配置,預設開啟,若頁面中有錨點設計,需要將該配置刪除,否則觸發錨點會多觸發 $pageview 事件
heatmap: {
/**
* Web 元素點選($WebClick)
* 是否開啟點選圖,default 表示開啟,自動採集 $WebClick 事件,可以設定 'not_collect' 表示關閉。
* 預設只有點選 a input button textarea 四種元素時,才會觸發 $WebClick 元素點選事件
*/
clickmap: 'default',
/**
* 視區停留事件($WebStay)
* 是否開啟觸達圖,default 表示開啟,自動採集 $WebStay 事件,可以設定 'not_collect' 表示關閉。
* 需要 Web JS SDK 版本號大於 1.9.1
*/
scroll_notice_map: 'default',
// 通過 collect_tags 配置是否開啟其他任意元素的全埋點採集(預設不採集),其中 div 通過配置最多可以採集 3 層巢狀情況。
collect_tags: {
div: {
max_level: 3, // 預設是 1,即只支援葉子 div。可配置範圍是 [1, 2, 3],非該範圍配置值,會被當作 1 處理。
},
li: true,
span: true,
i: true,
img: true
},
},
})
// 註冊公共屬性
window.cassSensors.registerPage({
platform_type: 'web',
path_name: window.location.pathname,
})
/**
* Web 頁面瀏覽($pageview)
* 設定之後,SDK 就會自動收集頁面瀏覽事件,以及設定初始來源。
*/
window.cassSensors.quick('autoTrack');
/** 獲取使用者登入ID,使用者登入後,開發人員呼叫login,將使用者登入ID傳給SDK,後續該裝置上所有事件的distinct_id就會變成使用者所對應的登入ID。*/
ajax({
url: '/webim/user/jwt_token',
type: 'GET',
success: function (res) {
try {
var data = JSON.parse(res);
window.cassSensors.login(data.username);
} catch (e) { }
},
error: function (error) { }
});
}
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
var explorer = window.navigator.userAgent;
if (explorer.indexOf('MSIE') >= 0) {
// ie
script.onreadystatechange = function () {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
addShenCeScript();
}
}
} else {
// chrome
script.onload = function () {
addShenCeScript();
}
}
script.setAttribute(
'src',
'https://mstatic.cassmall.com/assets/sensors/sensorsdata1.19.4.min.js'
);
/** 封裝ajax請求 */
function ajax(params) {
params = params || {};
params.data = params.data || {};
// 判斷是ajax請求還是jsonp請求
var json = params.jsonp ? jsonp(params) : json(params);
// ajax請求
function json(params) {
// 請求方式,預設是GET
params.type = (params.type || 'GET').toUpperCase();
// 避免有特殊字元,必須格式化傳輸資料
params.data = formatParams(params.data);
var xhr = null;
// 例項化XMLHttpRequest物件
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
// IE6及其以下版本
xhr = new ActiveXObjcet('Microsoft.XMLHTTP');
};
// 監聽事件,只要 readyState 的值變化,就會呼叫 readystatechange 事件
xhr.onreadystatechange = function () {
// readyState屬性表示請求/響應過程的當前活動階段,4為完成,已經接收到全部響應資料
if (xhr.readyState == 4) {
var status = xhr.status;
// status:響應的HTTP狀態碼,以2開頭的都是成功
if (status >= 200 && status < 300) {
var response = '';
// 判斷接受資料的內容型別
var type = xhr.getResponseHeader('Content-type');
if (type.indexOf('xml') !== -1 && xhr.responseXML) {
response = xhr.responseXML; //Document物件響應
} else if (type === 'application/json') {
response = JSON.parse(xhr.responseText); //JSON響應
} else {
response = xhr.responseText; //字串響應
};
// 成功回撥函式
params.success && params.success(response);
} else {
params.error && params.error(status);
}
};
};
// 連線和傳輸資料
if (params.type == 'GET') {
// 三個引數:請求方式、請求地址(get方式時,傳輸資料是加在地址後的)、是否非同步請求(同步請求的情況極少);
xhr.open(params.type, params.url + '?' + params.data, true);
xhr.send(null);
} else {
xhr.open(params.type, params.url, true);
//必須,設定提交時的內容型別
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
// 傳輸資料
xhr.send(params.data);
}
}
//格式化引數
function formatParams(data) {
var arr = [];
for (var name in data) {
// encodeURIComponent() :用於對 URI 中的某一部分進行編碼
arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
};
// 新增一個隨機數引數,防止CDN快取
arr.push('v=' + random());
return arr.join('&');
}
// 獲取隨機數
function random() {
return Math.floor(Math.random() * 10000 + 500);
}
}
if (!window.cassSensors) {
document.getElementsByTagName('head')[0].appendChild(script);
}
})()