埋点方法
曝光埋点在各类埋点中是最复杂的。虽然点击埋点可能只是代码实现上增加了繁琐,但曝光埋点涉及的内容更加广泛,且如果实现不当,准确性可能会受到显著影响。
在滚动视图中,曝光检测通常依赖于监听滚动事件,结合 DOM 接口获取元素的实时坐标,计算其是否与可视区域重叠以判断是否“可见”。这种方法的准确性较高,且兼容性良好,适用于大多数场景。然而,它的缺点在于计算量大,性能损耗显著。实时监听滚动事件并进行计算,即使采用防抖或节流技术,仍会消耗较多资源。在首屏加载或复杂页面中,用户的滑动操作可能会导致频繁触发计算,从而引发性能问题,造成页面卡顿。
(此外,复杂性较高,例如数据刷新后需要重新计算,即使同一元素位置不同内容的展示,曝光也需分别计算。 — 怎么解决呢? )
因此,使用 IntersectionObserver
进行曝光处理是一个更优的选择。IntersectionObserver
是 Chrome 在 2016 年推出的新特性,目前已被主流浏览器广泛支持。它可以异步检测元素的可见性,无需频繁调用事件监听。当观察的元素交叉状态发生变化时,会自动触发回调函数。这种方法不仅提高了性能,还能有效应用于懒加载和无限滚动等场景。
小程序端
let timerMap = new Map()
Page({
onLoad() {
// component一般需要传当前页面或组件实例
this._observer = wx.createIntersectionObserver(this, {
thresholds: [0.5],
// observeAll: true // 如果有多个item,可一次性绑定相同class
})
this._observer = this._observer.relativeTo('.scroll-view')
this._observer.relativeToViewport({
// bottom: -400 设置边距
}).observe('#green', res => {
console.log('observe', res)
if (res.intersectionRatio > 0.5) {
timerMap.set('#green', setTimeout(() => {
console.log('有效曝光!', res)
// report(res.dataset) 上报埋点
}, 1500))
} else { // 1.5s内触发离开回调函数,则取消上报
clearTimeout(timerMap.get('#green'));
timerMap.delete('#green');
console.log('离开可视区', res)
}
})
},
onUnload() {
if (this._observer) this._observer.disconnect()
for (let [key, timerId] of timerMap) {
clearTimeout(timerId);
}
timerMap.clear();
}
})
web端
// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('元素进入视口:', entry.target);
// 可以在这里执行懒加载或其他操作
} else {
console.log('元素离开视口:', entry.target);
}
});
}, {
root: null, // 视口为浏览器窗口
rootMargin: '0px', // 根元素的边距
threshold: 0.1 // 交叉比例阈值
});
// 观察目标元素
const targetElement = document.querySelector('.target');
observer.observe(targetElement);
// web端由于需要先获取dom实例进行绑定,因此,我们可以利用 WeakMap 的弱引用特性,将 DOM 实例作为键存储相关的定时器,这样在页面销毁时,与之绑定的定时器会自动被清理,无需手动管理。
const timerMap = new WeakMap();
// 设置定时器并存储引用
timerMap.set(element, setTimeout(() => {
// 处理逻辑
}, 1000));
// 当元素被销毁时,WeakMap 中的项会被自动清除