前言
本文讲述的是在 kbone vue 搭建的多页面的小程序框架中,如何通过 vue 指令符的方式进行元素的单次曝光和曝光时长的埋点上报方案和具体的实现。
方案整体流程图
下面对整体流程进行具体代码的实现
绑定 vue 指令到元素上
1. 创建 vue 指令 reporte
export default (Vue) => {
Vue.directive('report', {
bind: (el, binding) => {
// todo...
},
inserted: (el, binding) => {
// todo...
},
update: (el, binding) => {
// todo...
},
unbind: (el) => {
// todo...
},
});
};
2. 在 Vue 全局使用此指令
Vue.use(directive)
编写指令生命周期函数,对元素行为进行监听,完成流程图的后三步
1. bind 函数
bind: (el, binding) => {
// 获取 v-report 上绑定的事件
const { exposure } = binding.modifiers;
// 获取传入的参数
const { value } = binding;
// 把参数赋值到 el 的 $__dataset 属性上,让数据不丢失
el.$__dataset = value;
// 监听曝光事件
if (exposure) {
// 创建曝光计时器
const spm = new SPM();
spm.setTime(Date.now());
// 把 timer 计时器绑定到 el 上
el.spmTimer = spm;
// 当 wxhide 时,判断元素是否曝光了,曝光了上报一次曝光时间,关键步骤
window.addEventListener('wxhide', () => {
reportDurationUnshow(el, binding);
});
// 当 wxunload 时,判断元素是否曝光了,曝光了上报一次曝光时间,关键步骤
window.addEventListener('wxunload', () => {
reportDurationUnshow(el, binding);
});
}
}
2. update 函数
update: (el, binding) => {
// 当 binding 中的 value ,即传入的值更新时,把最新的值绑定到 $__dataset 上,上报时取得的就是最新的参数了
const { value } = binding;
el.$__dataset = value;
},
3. inserted 函数,进行上报行为的最主要函数
inserted: (el, binding) => {
const { exposure } = binding.modifiers;
const { value } = binding;
// 获取绑定数据上的 className,用于创建监听函数时选中元素,需要保持此 className 在上报页面的唯一性
const { className } = value || {};
// 监听单次曝光事件
if (exposure) {
// 创建监听函数
const observer = window.$$createIntersectionObserver();
// 绑定创建的 IntersectionObserver 到 el 上,unbind 时 disconnect,防止内存泄漏
el.IntersectionObserver = observer;
setTimeout(() => {
// .h5-body >>> .${className} 选中元素进行监听函数的绑定
observer.relativeToViewport().observe(`.h5-body >>> .${className}`, (res) => {
// 判断元素是否出现在屏幕可视范围内
if (res.intersectionRatio > 0) {
// 获取上报的参数,进行上报
const { $__dataset: params } = el;
// 元素单次曝光上报函数 report 根据自己的业务进行封装
report(`impression`, getReportData(params));
// 设置元素是否曝光,用于在页面 wxhide 或者 wxunload 时对已曝光元素进行一次曝光时长上报
el.isExposure = true;
} else {
// 元素是从页面可视范围移出,进行曝光时长上报,上报函数自己封装
reportDuration(el, binding);
// 把元素是否曝光的标识设置为 false
el.isExposure = false;
}
});
}, 0);
}
},
4. unbind 函数绑定
// 绑定的元素销毁时才会触发,只要页面在小程序页面栈中且元素没有销毁就不会触发
unbind: (el) => {
const { IntersectionObserver } = el;
// 存在 IntersectionObserver, disconnet 释放内存
IntersectionObserver && IntersectionObserver.disconnect();
},
5. 计时器的简单 demo
// 代码简单,不再解释
export default class SPM {
constructor() {
this.timer = 0;
}
setTime(timestamp) {
this.timer = timestamp || Date.now();
}
getDuration() {
if (this.timer === 0) {
return 0;
}
const now = Date.now();
const duration = now - this.timer;
this.timer = now;
return duration;
}
}
解释点
-
window.$$createIntersectionObserver 函数可参考 kbone 上的说明:wechat-miniprogram.github.io/kbone/docs/… 也是进入微信小程序 wx.createIntersectionObserver 的解释文档
-
为什么需要把上传参数绑定到 el 上?
是为了实现不同生命周期方法数据的传输。因为其中涉及到数据的更新。在 bind 函数上绑定的方法,即使数据有更新,因为在 update 函数中重新绑定可数据,也能拿到最新的数据(此现象在这个案例中没有体现,以后会写一个关于 click 事件的指令埋点上报,会出现上述说的情况)。
使用
// 示例代码
<span
@click="handleClick"
class="equal-class-name"
v-report.exposure="{
className: 'equal-class-name',
extraData,
...
}"
>
{{ buttonText }}
</span>
- 首先在元素上绑定唯一的 class 'equal-class-name'
- 指令符的传入参数中把 className 属性设置为对应的 'equal-class-name'
解释:
(1)extraData 不是必传参数,看业务场景想传什么就传什么
结束
完成上述步骤就可以在页面上通过 v-report.exposure 指令的方式进行曝光事件的上报了。
除了曝光事件,还可以在指令的生命周期函数进行其他事件的上报,如 click。可以采用 v-report.click.exposure ,同时对 click 和 exposure 事件进行上报。click 事件相对简单很多,所以没有进行说明。
tips
文章埋点的思路同样适用于用 vue 开发的项目,只需要对其中的一些点进行改动,如监听函数可以改为浏览器的 IntersectionObserver 函数进行创建。
其实单词曝光和曝光时长可以合并为只进行曝光时长的上报,这样可以减少请求的发送量。