前段时间在开发过程中有一个埋点需求,一开始还不知道什么是埋点,在一番了解过后有所见解,故在此分享!(本次分享主要以React项目为背景,事实上主要还是思想,基本没有使用过多的框架提供的API。)
首先来解释何为埋点:
- 埋点(Burying Point,BP)就是将用户在网站中的某些交互行为进行上报,从而收集对某些功能、页面的访问情况进行收集,进一步为产品优化、数据分析提供支持。
- 举个例子:项目中新上线了一个页面,该页面为用户提供了某些信息的展示,由于是新页面,我们想知道这个页面在一段时间内用户的访问量以及具体使用了哪些功能进行数据收集,以针对性的优化页面。此时就需要这个新页面以及某些交互功能进行埋点了。
埋点的流程实际上十分简单:用户交互行为
-->携带数据发送请求
那么我们需要明确以下几点:
- 如何监听用户交互行为?
- 如何收集数据?
- 何种方式发送请求?
如何监听用户交互行为?
实际上很简单,埋点可以分为两类
- 对某个功能的埋点
- 一类是对页面访问的埋点,即PV(Page View)埋点
对某个功能的埋点
用户对想要使用某个功能,一定离不开触发某个事件。以onClick
事件为例,我们可以在项目的入口处添加事件监听器来监听整个项目的onClick
事件;
js
useEffect(() => {
//监听点击事件,触发埋点请求
document.addEventListener('click', report);
return () => {
document.removeEventListener('click', report);
};
}, []);
其中report
函数为获取埋点信息并进行上报,会在后面提到。
对某个页面访问的埋点
对于PV埋点来说,实现方式会更加简单,我们只需要在路由发生变化时进行数据上报即可; 以React为例,我们可以利用React Router,当路由地址发生变化时,通过对路由地址过滤,有针对性地上报数据。 PV埋点不同于点击埋点,下一节收集数据仅针对点击埋点,实际PV埋点到此处就已经介绍完了,我们需要做的只是在路由切换时对不同路由进行过滤以及发送请求即可。
如何收集数据
在这里我们使用的方案是通过HTML标签的自定义数据属性data-*
携带数据,并通过从事件源递归向上寻找该标签进而发送请求。
在项目的入口处,我们监听了click
事件并触发了report()
函数,这个函数的功能就是:
- 递归寻找带有
data-bp
的标签 - 发送请求,上报数据(见下一节)
递归寻找带有data-bp
的标签:
js
//从事件源递归向父级元素寻找 data-bp 属性
const collectBpInfo = (dom) => {
if (!dom || dom.getAttribute('id') === 'root') {
return '';
}
//判断data-bp是否为空
if (!isBlank(dom.getAttribute('data-bp'))) {
return dom.getAttribute('data-bp');
}
return collectBpInfo(dom.parentElement);
};
const report = (e) => {
const bpInfo = collectBpInfo(e.target);
if (isBlank(bpInfo)) {
return;
}
//发送请求,上报数据
...
};
至此,我们就监听到了项目中的所有点击事件,并且收集了数据 只差最后一步发送请求了。
何种方式发送请求
通常发送请求我们首选就是axios,但实际上浏览器为我们提供了一个更适用于埋点场景的API:
-->Navigator.sendBeacon(url,data)
详见MDN
这种方式的好处在于,不会由于用户的某些操作而中断了请求的发送(页面卸载...),并且这是一个异步的post请求,浏览器会在合适的时机将其发送给服务器。
总结
至此,我们就实现了一个基本的埋点需求,当我们想要在某些点击事件的功能上触发埋点,只需要像这样: <button data-bp="info" ...>...</button>
即可。
这种方式虽然在后续增删埋点时十分方便,但是也带来一定问题:
- 递归向上搜索可能会由于项目结构过于复杂,递归调用栈过多而导致栈溢出
- 所有点击事件都会触发这个
report()
函数
但是这些性能消耗在中小项目中带来的影响并不大。 如果为了更好的解决这个问题,我们只有在需要触发埋点的地方主动发送请求,这就有点麻烦了,可以使用自定义Hook来简化,但也简化不了多少,可以看看这篇(React 实现自动上报 pv/click 的埋点 hooks)。
总之对于埋点问题,方案很多,但是没有一个适用于所有情况的方案(没有"银弹"),对于小项目中还是很推荐以上这种方案的,一劳永逸嘛!