应用中特定的流程收集一些信息,用来跟踪应用使用的状况,以便后续进一步优化产品或者提供运营的数据支撑。如访问数,访客,停留时常,页面浏览数等
事件数据收集 页面访问量收集 使用监听url变化, 如果是history,则可以监听pushstate 和replacestate
hashchange 页面停留时间收集
页面停留时间,就是记录从访问页面到离开/关闭页面的时间。不管是SPA应用还是非SPA应用,都是记录从页面加载到页面关闭或跳转的时间,
load => 单页面应用 replaceState, pushState, 非单页面应用popstate
代码如下:
// 首次进入页面 window.addEventListener("load", () => { // 记录时间 const
time = new Date().getTime(); dulation.startTime = time; });
// 单页应用页面跳转(触发 replaceState) window.addEventListener("replaceState",
() => { const time = new Date().getTime(); dulation.value = time
- dulation.startTime;
// 上报 // .....
// 上报后重新初始化 dulation, 以便后续跳转计算 dulation.startTime = time;
dulation.value = 0; });
// 单页应用页面跳转(触发 pushState) window.addEventListener("pushState", () =>
{ const time = new Date().getTime(); dulation.value = time -
dulation.startTime;
// 上报 // .....
// 上报后重新初始化 dulation, 以便后续跳转计算 dulation.startTime = time;
dulation.value = 0; });
// 非单页应用跳转触发 popstate window.addEventListener("popstate", () => {
const time = new Date().getTime(); dulation.value = time -
dulation.startTime;
// 上报 // .....
// 上报后重新初始化 dulation, 以便后续跳转计算 dulation.startTime = time;
dulation.value = 0; });
// 页面没有任何跳转, 直接关闭页面的情况 window.addEventListener("beforeunload", () =>
{ const time = new Date().getTime(); dulation.value = time -
dulation.startTime;
// 上报 // .....
// 上报后重新初始化 dulation, 以便后续跳转计算 dulation.startTime = time;
dulation.value = 0; });```
## 异常数据收集
```window.addEventListener("error", (e) => { console.log("上报",
"error"); });
window.addEventListener("unhandledrejection", (e) => {
console.log("上报", "unhandledrejection"); }); ```
{
uid; // 用户id
title; // 页面标题
url; // 页面的url
time; // 触发埋点的时间
ua; // userAgent
screen; // 屏幕信息, 如 1920x1080
type // 数据类型,根据触发的不同埋点有不同的类型
data; // 根据不同的type,此处的数据也不同,如 事件数据有元素名称,事件名称、页面停留时间有停留时长....
sdk // sdk 相关信息
}
function send(data) {
const url = `xxxx`
if (navigator.sendBeacon) {
navigator.sendBeacon(url, JSON.stringify(data));
} else {
const imgReq = new Image();
imgReq.src = `${url}?params=${JSON.stringify(
data
)}&t=${new Date().getTime()}`;
}
}
// JS 完整代码部分
(function (e) {
function wrap(event) {
const fun = history[event];
return function () {
const res = fun.apply(this, arguments);
const e = new Event(event);
window.dispatchEvent(e);
return res;
};
}
class TrackingDemo {
constructor(options = {}) {
// 重写 pushState、replaceState
window.history.pushState = wrap("pushState");
window.history.replaceState = wrap("replaceState");
// 上报地址
this.reportUrl = options.reportUrl || "";
this.sdkVersion = "1.0.0";
this._eventList = ["click", "dblclick", "mouseout", "mouseover"];
this._dulation = {
startTime: 0,
value: 0,
};
this._initJSError();
// 初始化事件数据收集
this._initEventHandler();
// 初始化PV统计
this._initPV();
this._initPageDulation();
}
setUserId(uid) {
this.uid = uid;
}
_initEventHandler() {
this._eventList.forEach((event) => {
window.addEventListener(event, (e) => {
const target = e.target;
const reportKey = target.getAttribute("report-key");
if (reportKey) {
this._report("event", {
tagName: e.target.nodeName,
tagText: e.target.innerText,
event,
});
}
});
});
}
_initPV() {
window.addEventListener("pushState", (e) => {
this._report("pv", {
type: "pushState",
referrer: document.referrer,
});
});
window.addEventListener("replaceState", (e) => {
this._report("pv", {
type: "replaceState",
referrer: document.referrer,
});
});
window.addEventListener("hashchange", () => {
this._report("pv", {
type: "hashchange",
referrer: document.referrer,
});
});
}
_initPageDulation() {
let self = this;
function initDulation() {
const time = new Date().getTime();
self._dulation.value = time - self._dulation.startTime;
self._report("dulation", {
...self._dulation,
});
self._dulation.startTime = time;
self._dulation.value = 0;
}
// 首次进入页面
window.addEventListener("load", () => {
// 记录时间
const time = new Date().getTime();
this._dulation.startTime = time;
});
// 单页应用页面跳转(触发 replaceState)
window.addEventListener("replaceState", () => {
initDulation();
});
// 单页应用页面跳转(触发 pushState)
window.addEventListener("pushState", () => {
initDulation();
});
// 非单页应用跳转触发 popstate
window.addEventListener("popstate", () => {
initDulation();
});
// 页面没有任何跳转, 直接关闭页面的情况
window.addEventListener("beforeunload", () => {
initDulation();
});
}
_initJSError() {
window.addEventListener("error", (e) => {
this._report("error", {
message: e.message,
});
});
window.addEventListener("unhandledrejection", (e) => {
this._report("error", {
message: e.reason,
});
});
}
// 用户可主动上报
reportTracker(data) {
this._report("custom", data);
}
_getPageInfo() {
const { width, height } = window.screen;
const { userAgent } = navigator;
return {
uid: this.uid,
title: document.title,
url: window.location.href,
time: new Date().getTime(),
ua: userAgent,
screen: `${width}x${height}`,
};
}
_report(type, data) {
const reportData = {
...this._getPageInfo(),
type,
data,
sdk: this.sdkVersion,
};
if (navigator.sendBeacon) {
navigator.sendBeacon(this.reportUrl, JSON.stringify(reportData));
} else {
const imgReq = new Image();
imgReq.src = `${this.reportUrl}?params=${JSON.stringify(
reportData
)}&t=${new Date().getTime()}`;
}
}
}
e.TrackingDemo = TrackingDemo;
})(window);
// HTML 代码部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button report-key="button">按钮</button>
</body>
<script src="./tackerDemo.js"></script>
<script>
const trackingDemo = new TrackingDemo()
</script>
</html>
工程化