前端代码埋点实现
1. 环境及需求介绍
- 环境:
Vue2.7
- 需求:全页面访问、事件点击等全监听
2. 封装逻辑
- 由于需要监听页面访问事件,为了统一封装,我们将用到
Mixins
混入监听页面的生命周期; - 所有的事件发送参数分为公参和特定事件参数,因此我们需要将公参数据进行提取,统一修改。
- 埋点的核心逻辑,用到的是 GIF 请求发送
使用 GIF 请求发送的原因其实很好理解:
防止跨域问题:
为了防止数据请求收发的的地址和项目当前地址不同,而产生跨域问题。所以我们一般使用
<img src='xxx'>
图片进行数据请求。防止阻塞页面加载,影响用户体验:
由于创建资源节点,需要将对象注入到浏览器 DOM 树,浏览器才会发送数据请求。但是仿佛操作 DOM 会产生性能问题,并且反复载入
js/css
资源会阻塞页面渲染。但是 图片请求 能够很好的解决这个问题。在JavaScript
中new
一个image
对象就能发起数据请求,并且不会产生阻塞问题。GIF 体积最小
与常见的 Jpg 和 PNG 格式的图片相比,GIF 的体积最小:PNG 需要 67 个字节,而 GIF 只需 43 个字节。
3. 封装基础库
封装
Debug
函数由于模块间的独立性,我们将打印数据埋点的
Debug
进行统一封装:/** * 控制台输出信息 * @param {...any} args 输出信息 */ const DEBUG_LOG = true // 是否输出采集信息 export function debug(...args) { if (DEBUG_LOG) console.log(...args) }
GIF 核心请求模块
const QS = require('querystring') const apiGIF = 'http://${project}.${host}/logstores/${logstore}/track.gif' /** * @function 具体的track动作 * @param {Object} params 具体发送的数据 * @param {String} apiUrl 发送的接口请求 */ export function trackAction(params, apiUrl = apiGIF) { /* 如果参数不是字符串则转换为query-string */ let _params = typeof params === 'string' ? params : QS.stringify(params) /* 创建Image对象来发送请求 */ /* <img src='http://${project}.${host}/logstores/${logstore}/track.gif?APIVersion=0.6.0&key1=val1&key2=val2'/> */ let src = `${apiUrl}?${_params}` let img = new Image(1, 1) debug('发送日志, 内容参数为: ', QS.parse(_params)) img.src = src /* 请求发送, load和error事件监听动作的完成 */ return new Promise((resolve, reject) => { img.onload = function () { resolve({ code: 200, data: 'success!' }) } img.onerror = function (e) { reject(new Error(e.error)) } }) }
发送日志实际函数
const priorParameters = { // 公共参数 count_version: '1.0.0', // 统计版本号 app_version: '1.0,0', // apk应用版本号 ..... } /** * 获取当前时间 */ export function getCurrentTime() { return new Date().valueOf() } /** * 发送日志 * @param {Object} trackData 特定信息 */ export function triggerLog(trackData) { let session_id = getCurrentTime() // 毫秒级时间戳,单次使用 trackData = { ...priorParameters, session_id, ...trackData } trackData = QS.stringify(trackData) return trackAction(trackData) }
由此,我们封装好了,埋点数据收发的主体函数,后续只需要依据特定事件,调用
triggerLog
函数传输特定参数trackData
就完成了埋点的数据请求。利用
Mixins
混入封装页面返回事件如点击事件等页面详细事件,不同项目可以做到不同封装,在此不做具体分析了。我们来看看页面返回事件的统一封装。
混入(
Mixins
) 可以捕获页面的生命周期,由于切换页面会触发页面的生命周期,因此,我们可以借此完成页面访问的埋点。Tips 生命周期遵从“从外到内,再从内到外,mixins 先于组件”的原则
所以,页面生命周期会先加载
Mixins
,而后若有相同的事件,则由页面内部进行覆盖。最终的
trackMixins
如下:/** * @module 组件混入埋点 */ import { trigger, debug } from './trackBase' export const trackMixins = { data() { return { __is_first_create: true, // 是否初次访问 } }, created() { debug('-----------页面创建初始化-----------') const pageName = this.$route.name this.__triggerPageview() }, activated() { if (this.$data.__is_first_create) { // 首次访问, 不执行该初始化 this.$data.__is_first_create = false return } debug('-----------页面重新挂载-----------') this.__triggerPageview() }, beforeRouteUpdate(to, from, next) { debug('-----------页面即将刷新-----------') this.__triggerPageview() // 发送刷新前当页信息事件 next() debug('-----------页面刷新-----------') this.__pageRefresh() // 页面刷新, 发送页返事件 }, beforeRouteLeave(to, from, next) { debug('-----------页面关闭-----------') this.__triggerPageview() // 发送页面关闭事件 next() }, methods: { /* ---------页面点击事件---------- */ mixinTriggerClick(clickData) { let trackData = { // xxxxx } debug('-----------发送点击事件-----------') return trigger(trackData) }, /* ------------------- 内部封装事件方法 ------------------- */ /* ---------触发页面访问事件---------- */ __triggerPageview() { // 用户进行页面访问,离开页面时发送该统计请求; 主要用途:记录用户在页面上的浏览操作行为 let trackData = {} // 此处省略时间处理函数 return trigger(trackData) }, __pageRefresh() { // 页面内容更新 this.__triggerPageview() }, }, }
如上,我们还将页面点击事件进行了封装。实际项目,我们依据需要进行处理。
具体页面使用
<template></template> <script> import { trackMixins, trigger } from '@/log/index.js' // 导入埋点组件 export default { mixins: [trackMixins], // ..... } </script>
前端代码埋点还是需要依据不同的项目需要,进行设计。核心逻辑依旧是封装 GIF 请求的 src 发送。
笔者只是提供一个思路,感谢你的浏览。
以上。