记录一下对于app的sdk的开发
为了能够在app端记录用户的访问轨迹和行为,我们需要进行埋点,也需要开发属于项目的sdk,分享一下开发的思路、代码以及很多有用的集成方法。
思路
和微信的全面sdk不同,我们的sdk就是集成的共需方法,用于统计用户的各种所需要统计的行为,为了兼容各个版本或者项目,所以代码书写都需要考虑兼容性,所以基本都是基于原生代码。
(function(window) {
SDK = {};
var sTime = _sTime || new Date();
// 默认参数
var df = {
};
// 功能函数
var skill = {
gStayTime: function() {
var under = new Date();
return under - sTime;
}
}
// 一些工具函数
var common = {
cookie: {
get: function(name) {
return (name = RegExp("(^| )" + name + "=([^;]*)(;|$)").exec(document.cookie)) ? name[2] : null
},
/**
* 设置 cookie
* @param {string} name cookie name
* @param {string} key cookie value
* @param {option} opt {expire: 1000, domain: '', path: '', Ra: true}
*/
set: function(name, key, opt) {
var expireTime;
opt.expire && (expireTime = new Date, expireTime.setTime(expireTime.getTime() + opt.expire));
document.cookie = name + "=" + key
+ (opt.domain ? "; domain=" + opt.domain: "")
+ (opt.path ? "; path=" + opt.path: "")
+ (expireTime ? "; expires=" + expireTime.toGMTString() : "")
+ (opt.Ra ? "; secure": "")
;
}
},
localStorage: {
check: function() {
},
set: function(name, val, expire) {
},
get: function() {},
remove: function() {}
},
sessionStorage: {
get: function() {},
set: function() {}
},
event: {
add: function(elem, name, listener) {
if (window.addEventListener) {
elem.addEventListener(name, listener, false);
}
else if (window.attachEvent) {
elem.attachEvent('on' + name, listener);
}
else {
elem['on' + name] = listener;
}
},
del: function(elem, name, listener) {
if (elem.removeEventListener) {
elem.removeEventListener(name, listener, false);
} else if (elem,detachEvent) {
elem.detachEvent('on' + name, listener);
} else {
elem['on' + name] = null;
}
}
},
lang: {
checkType: function() {},
checkNumber: function() {},
checkString: function() {}
},
send: {
encodeURI: function(url) {
return escape(url).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F');
},
log: function(url, cb, params) {
var logImg = new Image;
var randomT = (new Date()).getTime() % ( 3600 * 1000);
rName = 'logFromJS' + Math.floor(2147483648 * Math.random()).toString(36) + randomT;
window[rName] = logImg;
logImg.onload = logImg.onerror = logImg.onabort = function() {
logImg.onload = logImg.onerror = logImg.onabort = null;
logImg = window[rName] = null;
cb && cb(url);
};
// 若有参数,添加参数
var urlParams = [];
var postData = '';
if (params) {
for (key in params) {
if (Object.prototype.hasOwnProperty.call(params, key) === true) {
urlParams.push('' + key + '=' + encodeURI(params[key]));
}
}
}
urlParams.push('random=' + rName);
postData = urlParams.join('&');
url = postData === '' ? url : (url + '?' + postData);
logImg.src = url;
},
/**
* 封装 ajax
* @param {obj} opt .method.url.async.data.success
* @return {null}
*/
ajax: function(opt) {
opt = opt || {};
opt.method = opt.method.toUpperCase() || 'POST';
opt.url = opt.url || '';
opt.async = opt.async || true;
opt.data = opt.data || null;
opt.success = opt.success || function() {};
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject('microsoft.XMLHTTP');
}
var params = [];
var key;
for (key in opt.data) {
if (Object.prototype.hasOwnProperty(opt.data, key) === true) {
params.push(key + '=' + opt.data[key]);
}
}
var postData = params.join('&');
if (opt.method.toUpperCase() === 'POST') {
xmlhttp.open(opt.method, opt.url, opt.async);
xmlhttp.setRequestHeader('Content-Type', 'application/json');
xmlhttp.send(postData);
} else if (opt.method.toUpperCase() === 'GET') {
xmlhttp.open(opt.method, opt.url + '?' + postData, opt.async);
xmlhttp.send(null);
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
opt.success(xmlhttp.responseText);
}
}
}
},
url: {
gUrlParm: function() {},
gProtocol: function() {},
gDomain: function() {},
gDomainNoPort: function() {},
gUrlPathname: function() {}
}
}
/**
* 浏览器加载完成时执行对应的函数
* @param {function} 待执行函数
*/
var domready = (function () {
var fns = [], listener
, doc = document
, hack = doc.documentElement.doScroll
, domContentLoaded = 'DOMContentLoaded'
, loaded = (hack ? /^loaded|^c/ : /^loaded|^i|^c/).test(doc.readyState)
;
if (!loaded) {
common.event.add(doc, domContentLoaded, listener = function() {
common.event.del(doc, domContentLoaded, listener);
loaded = 1;
while(listener = fns.shift()) listener()
})
common.event.add(window, 'load', listener = function() {
loaded = 1;
while(listener = fns.shift()) listener();
})
}
return function (fn) {
loaded ? setTimeout(fn, 0) : fns.push(fn)
}
})()
SDK.skill = skill
SDK.common = common;
SDK.domready = domready;
;(function() {
SDK.domready(function() {
// 获取cookie, 若存在,更新expires, 否则重新计算唯一标识然后存储
var ud = SDK.common.cookie.get('ud');
if (ud) {
SDK.common.cookie.set('ud', ud, {expire: 365 * 24 * 60 * 60 * 1000});
SDK.df.ud = ud;
} else {
var finger = new Date();
new Fingerprint2().get(function(result, components) {
console.log((new Date()).getTime() - finger);
var oldUd = SDK.common.cookie.get('ud');
SDK.common.cookie.set('ud', result, {expire: 365 * 24 * 60 * 60 * 1000});
SDK.df.ud = result;
})
}
// 获取 uuid, 若存在, 存储到df, 否则重新计算一个 uuid
var ussid = SDK.common.cookie.get('ussid');
if (ussid) {
console.log('ussid cookie 还保存');
SDK.df.ussid = ussid;
} else {
console.log('ussid cookie 失效');
var new_ussid = createUUID() + '$' + (new Date()).getTime()
SDK.common.cookie.set('ussid', new_ussid, {});
SDK.df.ussid = new_ussid;
}
// 页面关闭时
SDK.common.event.add(window, 'unload', function() { sendInfo({ at: 3 }); });
// 心跳计时器
function checkWindow() {
clearTimeout(timeHandle);
var ifVisible;
var stayTime = SDK.skill.gStayTime();
visiAttr && (ifVisible = 'visible' == document[visiAttr]);
hiddenAttr && (ifVisible = !document[hiddenAttr]);
if (ifVisible !== visibleState) {
visibleState = ifVisible;
}
if (stayTime <= 3600000) {
sendInfo({ at: 2 });
}
timeHandle = setTimeout(checkWindow, 5 * 1000);
}
// 兼容属性名称
function findAttr(attr) {
var doc = document;
l = '';
if (attr in doc) l = attr;
else {
for (var tmp = ['webkit', 'ms', 'moz', 'o'], index = 0; index < tmp.length; index++) {
var tmpAttr = tmp[index] + attr.charAt(0).toUpperCase() + attr.slice(1);
if (tmpAttr in doc) {
l = tmpAttr;
break;
}
}
}
return l;
}
// 设置指纹并发送加载完成事件
function startHeart(ud) {
SDK.common.cookie.set('ud', ud, {expire: 365 * 24 * 60 * 60 * 1000});
SDK.df.ud = ud;
sendInfo({ at: 1 });
checkWindow();
}
function createUUID() {
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
return uuid.split('-').join('');
}
var timeHandle,
visiAttr = findAttr('visibilityState'),
hiddenAttr = findAttr('hidden'),
visibleState = false,
beginTime = +new Date;
sendInfo({ at: 1 });
checkWindow();
})
})()
})(this)