埋點方案有命令式埋點和聲明式埋點。
- 命令式埋點:在用戶行為觸發位置調用事件上報函數進行行為上報,缺點是埋點和業務耦合度比較高,工作量比較大
- 聲明式埋點:通過自定義指令統一完成事件上報,使得埋點和業務代碼一定程度上解耦合。
本篇文章將記錄借助vue自定義指令完成聲明式埋點,降低前端埋點壓力。
一、准備工作
這里關於vue自定義指令和IntersectionObserver不做詳細介紹,自行前往官網了解學習。
- vue自定義指令,vue自定義指令官網
- IntersectionObserver實現元素視窗觀測,Intersection Observer API
二、代碼實現
- 創建指令
這里我們計划創建v-track
指令,創建代碼如下:
import Vue from 'vue'
import Exposure from './exposure'
import Click from './click'
// 實例化曝光和點擊
const exp = new Exposure()
const cli = new Click()
Vue.directive('track', {
// 調用指令聲明周期鈎子函數bind,其他鈎子函數請移步官網
bind(el, binding) {
// 獲取指令參數
const { arg } = binding
arg.split('|').forEach(item => {
// 點擊
if (item === 'click') {
cli.add({ el })
} else if (item === 'exposure') {
exp.add({ el })
}
})
}
})
- 曝光類
exposure.js
曝光使用IntersectionObserver觀察元素是否在視窗內,並且曝光上報只上報一次,上報之后移除觀察。設定每2秒進行一次上報。
如何解決曝光的漏報(定時器2秒之內的用戶退出)和多報:
a. 漏報:保存localStorage,下次進入之后如果有數據則上報,如果用戶再不進入,對漏報的幾條數據可忽略
b. 多報:IntersectionObserver監聽曝光,上報時候移除元素的監聽
import 'intersection-observer'
import { track } from './sendData'
// 節流時間調整,默認100ms
IntersectionObserver.prototype['THROTTLE_TIMEOUT'] = 300
export default class Exposure {
constructor(maxNum = 20) {
this.cacheDataArr = []
this.maxNum = maxNum
this._timer = 0
this._observer = null
this.init()
}
/**
* 初始化
*/
init() {
const self = this
// 邊界處理
this.trackFromLocalStorage()
this.beforeLeaveWebview()
// 實例化監聽
this._observer = new IntersectionObserver(function(entries, observer) {
entries.forEach((entry) => {
// 出現在視窗中
if (entry.isIntersecting) {
// 清除當前定時器
clearInterval(this._timer)
// 獲取參數
const tp = entry.target.attributes['track-params'].value
// 收集參數統一上報,減少網絡請求
self.cacheDataArr.push(tp)
// 曝光之后取消觀察
self._observer.unobserve(entry.target)
if (self.cacheDataArr.length >= self.maxNum) {
self.track()
} else {
self.storeIntoLocalStorage(self.cacheDataArr)
if (self.cacheDataArr.length > 0) {
// 2秒上報一次
self._timer = setInterval(function() {
self.track()
}, 2000)
}
}
}
})
},
{
root: null,
rootMargin: '0px',
threshold: 0.5 // 元素出現面積,0 - 1,這里當元素出現一半以上則進行曝光
})
}
/**
* 給元素添加監聽
* @param {Element} entry
*/
add(entry) {
this._observer && this._observer.observe(entry.el)
}
/**
* 埋點上報
*/
track() {
const trackData = this.cacheDataArr.splice(0, this.maxNum)
track(trackData)
// 更新localStoragee
this.storeIntoLocalStorage(this.cacheDataArr)
}
/**
* 存儲到localstorage, 防止在設定上報時間內用戶退出
* @param { Arrary } data
*/
storeIntoLocalStorage(data) {
window.localStorage.setItem('cacheTrackData', data)
}
/**
* 首次進入先獲取localStorage中的數據,也就是用戶上次退出未上報的數據
*/
trackFromLocalStorage() {
const cacheData = window.localStorage.getItem('cacheTrackData')
if (cacheData) {
track(cacheData)
}
}
/**
* 用戶退出系統時調用方法,需要和客戶端同學協商注冊事件
*/
beforeLeaveWebview() {
// 客戶端自定義事件監聽上報
}
}
- 點擊類
click.js
用戶的點擊行為沒有曝光行為頻繁,所以簡單處理,每次點擊進行埋點上報。
import { track } from './sendData'
export default class Click {
add(entry) {
const tp = entry.el.attributes['track-params'].value
entry.el.addEventListener('click', function() {
track(tp)
})
}
}
- 上報函數
sendData.js
上報函數未具體實現,如果需要提供,后續私信完善。
import config from './config'
/**
* 事件上報
* @param {Object} params
*/
export function track(params) {
// 這里自己封裝fetch或者axios,在攔截器中實現公共參數上報
console.log(`Track data to server ${config.serverUrl}: ${JSON.stringify(params)}`)
}
三、使用
- 引入全局指令
// main.js
import './directives/track'
- 頁面使用自定義指令完成上報
// 點擊事件
<div v-track:click></div>
// 點擊事件帶參數
<div v-track:click :track-params="12455"></div>
// 曝光事件
<div v-track:exposure></div>
// 曝光事件帶參數
<div v-track:exposure :track-params="12455"></div>
// 曝光事件並點擊帶參數
<div v-track:click|exposure :track-params="12455"></div>
以上,記錄vue項目如何進行聲明式埋點,不足之處望指正,不喜勿噴!思路借(chao)鑒(xi)於amandakelake/blog