Taro微信小程序实现元素曝光埋点上报

4 阅读1分钟

封装了自定义Hook

interface UseExposureProps {
  onExposure: (extra: Record<string, any>) => void;
  idPrefix: string;
  extra?: Record<string, any>;
  once?: boolean;
  thresholds?: number;
  relativeTo?: string;
  margins?: { top: number; bottom: number; left: number; right: number };
  throttleTime?: number;
}
/**
 * 曝光埋点Hook
 */
export function useExposure({
  onExposure, // 曝光回调
  idPrefix, // 唯一id前缀
  extra = {}, // 额外参数,可用于回传给曝光回调
  once = false, // 是否只曝光一次
  thresholds = 0.8,// 元素曝光触发阈值(比如漏出80%就触发)
  relativeTo = 'viewport',// 相对于视口还是指定区域
  margins = { top: 0, bottom: 0, left: 0, right: 0 }, // 偏移量
  throttleTime = 500, // 节流时间(可以不要其实)
}: UseExposureProps<any>) {
  const domId = useRef(getUniqueId(idPrefix));
  // 存储上一次曝光触发时间
  const lastExposureTimeRef = useRef(0);
  useEffect(() => {
    // 创建IntersectionObserver
    const observer = Taro.createIntersectionObserver(this, {
      thresholds: [thresholds], // 可见率超过阈值触发
    });
    relativeTo === 'viewport'
      ? observer.relativeToViewport(margins)
      : observer.relativeTo(relativeTo, margins);

    // 监听元素可见性变化,这里使用深度选择器,最外层改为自己项目的最外层
    observer.observe(`._pageWrap >>> .${domId.current}`, (res: any) => {
      // 可见率超过阈值触发回调
      if (res.intersectionRatio > thresholds) {
        // 节流防抖逻辑:检查是否在指定时间内已触发过
        const now = Date.now();
        if (
          throttleTime > 0 &&
          now - lastExposureTimeRef.current < throttleTime
        ) {
          return; // 在节流时间内,跳过本次触发
        }
        lastExposureTimeRef.current = now; // 更新最后触发时间
        // 触发外部回调 (由组件处理上报)
        onExposure(extra);
        // 清理观察器,只触发一次
        once && observer?.disconnect();
      }
    });

    // 组件卸载时清理观察器
    return () => {
      observer?.disconnect();
    };
  }, []);

  return { domId: domId.current };
}

使用时:

const { domId } = useExposure({
  idPrefix: 'goods',
  onExposure: () => {};
});

return(
 <View className={domId}></View>
)

一些坑:

  1. 获取domId的dom元素,需要使用Taro的深度选择器'>>>',要不然选择不到dom的,同理使用Taro.createSelectorQuery().select(),也是要使用深度选择器就能跨组件选中dom,这是个大坑
  2. useEffect的依赖项不要传入依赖,要不然会多次监听,重复触发
  3. 节流时间可以不要,没什么实际用