vue-uv-tracker
Version:
用于捕获点击、搜索框回车搜索、路由切换等埋点数据并上报到服务器的Vue插件
179 lines (168 loc) • 5.58 kB
JavaScript
/*
* @Author: CaoZhongQian
* @Date: 2023-01-27 22:11:21
* @LastEditors: CaoZhongQian
* @LastEditTime: 2023-02-25 22:41:36
* @FilePath: \vue-tracking\src\index.js
* @Description: 埋点数据上报Vue插件
*/
(function () {
var Axios = require('axios')
var VueTracking = {
// 产品名字
productName: 'unknown',
// 用户名称
username: 'unknown',
// 上报到后台存储的Http接口地址
reportUrl: '',
// axios http客户端
reportClient: null,
// 是否禁用上报
disabled: false,
// 是否启用控制台打印
console: false,
//vue-router实例
router: null,
install: function (Vue, options) {
this.productName = options.productName || this.productName
this.username = options.username || this.username
this.reportUrl = options.reportUrl || ''
this.disabled = options.disabled || false
this.console = options.console || false
this.router = options.router
this.reportClient = Axios.create({
baseURL: this.reportUrl,
withCredentials: false,
timeout: 3000
})
Vue.prototype.$sendTrack = (eventName, eventData) => {
this.reportTrackingData(eventName, eventData);
}
//注册指令
Vue.directive('track', {
bind: function (el, binding, vnode) {
el.addEventListener(binding.arg || 'click', () => {
const {
eventName,
eventData
} = binding.value;
vnode.context.$sendTrack(eventName, eventData);
});
}
});
//注册输入框回车搜索指令
Vue.directive('track-search', {
inserted: function (el, binding) {
el.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
const keyword = event.target.value;
const eventName = binding.value;
const eventData = {
keyword: keyword,
};
Vue.prototype.$sendTrack(eventName, eventData);
}
});
}
});
if (this.router) {
let router = this.router
router.beforeEach((to, from, next) => {
// 获取当前时间戳
const timestamp = new Date().getTime();
// 获取上一个界面的路由信息
const previousRoute = router.app.$route;
// 如果存在上一个界面的路由信息,则计算停留时间
if (previousRoute) {
const stayDuration = timestamp - router.app.$previousRouteLeaveTime;
const eventName = 'routeChange';
const eventData = {
from: previousRoute.path,
fromName: (previousRoute.meta && previousRoute.meta.title) ? previousRoute.meta.title : previousRoute.name,
to: to.path,
toName: (to.meta && to.meta.title) ? to.meta.title : to.name,
stayDuration: stayDuration,
};
Vue.prototype.$sendTrack(eventName, eventData);
}
// 更新上一个界面的离开时间戳
router.app.$previousRouteLeaveTime = timestamp;
next();
});
}
Vue.prototype.$tracker = this
Vue.prototype.$reportTrackingData = this.reportTrackingData
},
/**
* @description: 上报埋点数据
* @param {*} eventName 事件名
* @param {*} eventData 事件数据
* @return {*}
*/
async reportTrackingData(eventName, eventData) {
try {
const trackingData = this.getTrackingData(eventName, eventData)
await this.report(trackingData)
} catch (error) {
console.log('处理埋点数据出错')
console.log(error)
}
},
/**
* @description: 生成埋点数据报文
* @param {*} eventName
* @param {*} eventData
* @return {*}
*/
getTrackingData(eventName, eventData) {
return {
product: this.productName,
username: this.username,
href: window.location.href,
eventName: eventName,
eventData: eventData
}
},
/**
* @description: 上报埋点数据到后台服务
* @param {*} trackingData
* @return {*}
*/
async report(trackingData) {
if (!this.disabled) {
try {
// 将数据序列化单行字符串,保证body体的json到了nginx是单行的json内容,解决filebeat解析多行文本失败的BUG
const exceptionStr = JSON.stringify(trackingData)
await this.reportClient({
method: 'post',
data: exceptionStr,
headers: {
'Content-Type': 'application/json'
}
})
} catch (error) {
console.log('上报埋点数据出错')
console.log(error)
}
}
},
/**
* @description: 设置上报的用户信息,用于标识异常信息来自哪个用户
* @param {*} username
* @return {*}
*/
setUserName(username) {
this.username = username
}
}
if (typeof exports === 'object') {
module.exports = VueTracking
} else if (typeof define === 'function' && define.amd) {
define([], function () {
return VueTracking
})
} else if (window.Vue) {
window.VueExceptionCaptor = VueTracking
Vue.use(VueTracking)
}
})()