故事背景
故事要从去年讲起,当时在做一个埋点需求,交互是在一个弹窗(有确认/取消两个按钮),点确认后会弹窗不关闭,展示其他内容。需求是期望对弹窗的内容进行曝光,如图:
当时由于时间问题,采取的方案是硬编码,后期觉得写法太low,就在年底述职的时候立了个Flag,想要改进元素埋点相关方案。改着改着突然发现,其他项目也会偶尔有相关的需求,干脆就抽象封装为一套组件了。
思路
设计这玩意主要是奔着易扩展方向去设计的,因为不知道什么时候就会有新的需求功能。其次是易用、通用,只有易用且兼容大部分系统,其他项目才会去使用你的方案。
之前的想法是从整个项目角度出发,想通过在整个项目的入口(单页应用),监听全局事件去进行。后面发现不可行,难推进、难用、难维护。
因此,换个角度去思考,把角度放在 React.Element 上去看问题。因为在 React 中渲染的内容都可以认为是 React.Element,小到 span 元素,上至整个页面的 Class。从这个方向去设计,可以使得我们的元素曝光埋点更通用。
至于扩展性,在确定实现方案是从元素角度去实现后,那么这边就可以轻松想到使用 HOC 去实现,如图:
在 HOC 中,封装各个方案的逻辑,以后要扩展,直接新增逻辑即可,如下:
效果演示
本组件目前实现了5种效果,分别是:
- 元素渲染即曝光
- 元素进入到可视区进行曝光
- 元素进入到可视区的次数
- 元素渲染到销毁的时长,可以大略认为是该元素的停留时长
- 元素进入到可视区域到销毁的时长
支持部分的混合使用,比如同时处理统计时长+曝光,更多可以参考文档。
初始化
因为是贴合业务使用的,这边设计的自动发送埋点函数逻辑是贴合我们公司的规范设计的,大家使用的时候可以自行调整,源码会在最后放出。
import { initDig } from ‘element-dig’;
const getAllParams = (evtParams = {}, sourceParams = {}) => {
return {
event: evtParams.event,
uicode: evtParams.uicode,
action: {
user_id: xxxx,
user_name: xxxx,
……
},
};
}
// 作者公司业务规范 每个埋点有三种参数,分别是 埋点ID,埋点参数A,埋点参数B
const send = (evtId, evtParams = {}, sourceParams = {}) => { // 通用发送埋点请求
// evt:事件ID(evt_ID),evtParams 埋点中除了pid、action的字段,sourceParams 埋点参数中的action字段
window.send(evtId, getAllParams(evtParams, sourceParams)); // 调用真正的埋点发送,这边基于大家自身的业务去改造
}
initDig(send);
该HOC在组件初始化的时候会校验 send 函数的参数个数,如果非0,才认为是正常的函数,使用的时候可以自行改造,我的实现代码如下:
componentDidMount() {
if (this.getFuncParameters() === 0) {
console.warn('请注意,您未传入埋点发送函数,函数发送功能将不可用');
return;
}
....
}
const getFuncParameters = () => {
if (typeof window !== 'object') {
// 兼容 SSR 渲染模式报错问题
return 0;
}
const mathes = /[^(]+\(([^)]*)?\)/gm.exec(Function.prototype.toString.call(window[symbol]));
if (mathes[1]) {
const args = mathes[1].replace(/[^,\w]*/g, '').split(',');
return args.length;
}
return 0; // 返回 0 默认不执行
},
** 注意: **
初始化函数,只需使用一次即可,会自动将函数挂载到 window 上,组件后期会自动调用。推荐单页应用挂载到入口页,多页的话放在本页面的 didmount 生命周期。
元素渲染即曝光
<Exposure
digData={{
// 默认的模式就是加载即曝光
evt: 200000,
evtParams: {
event: 'ItemExpo',
uicode: '元素UIcode',
},
actionParams: {
special: '元素简单曝光',
},
}}
>
此处放入将被曝光的元素
</Exposure>
如图,刷新页面,关注第一个 console.log
元素进入到可视区进行曝光
<Exposure
mode={[‘viewonce‘]}
digData={{
evt: 200000,
evtParams: {
event: 'ItemExpo',
uicode: '元素UIcode',
},
actionParams: {
special: '元素可视区域内曝光',
},
}}
>
此处放入将被曝光的元素
</Exposure>
注意观察滚动后,新增的 console
元素进入到可视区的次数
<Exposure
mode={[‘view‘]}
digData={{
evt: 200000,
evtParams: {
event: 'ItemExpo',
uicode: '元素UIcode',
},
actionParams: {
special: '元素出现在可视区曝光',
},
}}
>
'注意观察控制台的action参数'
</Exposure>
可以看到通过上下滚动,使得该元素出现在屏幕中 3 次,因此返回的结果也是 3 。
元素渲染时长
<Exposure
mode={[‘ time‘]}
digData={{
evt: 200000,
evtParams: {
event: 'ItemExpo’,
uicode: '元素UIcode‘,
},
actionParams: {
special: '时长埋点,注意观察在action参数中的stt,单位是秒‘,
},
}}
>
'注意观察控制台的action参数'
</Exposure>
这里使用按钮触发元素销毁,手动触发统计时间。
元素进入到可视区域到销毁的时长
<Exposure
mode={[‘timeSinceView‘]}
digData={{
evt: 200000,
evtParams: {
event: 'ItemExpo’,
uicode: '元素UIcode‘,
},
actionParams: {
special: '时长埋点,注意观察在action参数中的stt,单位是秒‘,
},
}}
>
'注意观察控制台的action参数'
</Exposure>
该示例,是曝光元素进入到可视区后,才进行统计时间。
以上即是最基础的5种模式的演示,支持部分功能的组合使用~
文档&源码
github地址:github.com/dcison/elem…
** 注意 ** 必然是不能直接 down 了就使用的,除非你们业务埋点的发送函数与我司一样,哈哈哈哈哈~