小程序模块曝光埋点方法

5,994 阅读3分钟

我们在处理模块曝光埋点时,需要根据页面滚动的位置判断模块是否可见(被曝光)。Web 上传统方法是增加页面 scroll 监听事件,根据滚动位置与模块位置进行对比判断,小程序上也可以使用这种方法,但现在有更便捷优雅的替代方案 —— IntersectionObserver 对象。

IntersectionObserver 对象

IntersectionObserver 对象,用于推断某些节点是否可以被用户看见,下面将介绍相关的 API:

(1) 创建

通过 this.createIntersectionObserver 创建,该方法可传入的参数有三个:

  • thresholds:数值数组,代表相交比例的阈值(可有多个,取值范围[0,1]),当相交到达该阈值时会触发一次监听回调,在曝光埋点场景下设置为中间位置 [0.5] 即可;
  • initialRatio:初始相交比例,如果方法调用时检测到的相交比例与这个值不相等且达到阈值,则会触发一次监听器的回调函数,在曝光埋点的场景下设置为默认值0即可;
  • observeAll:是否同时观测多个目标节点;

(2) 设置参考区域

设置参考区域的方法有两个:io.relativeToViewport()io.relativeTo('selector', { ...margins }),如果判断相交参考区域是窗口,则使用前者,曝光埋点的场景下就使用这个;后者可用选择器设置其他节点作为相交的参考区域。

(3) 监听

开始监听方法:io.observe(selector, callback),selector代表目标模块的选择器,当它和参考区域相交达到阈值比例时,会触发 callback 回调函数,回调函数接受如下几个参数(在该场景中暂时都不会用到):

  • intersectionRatio: 两者相交比例;
  • time:相交检测时的时间戳;
  • 各种边界:
    • intersectionRect:相交区域的边界;
    • boundingClientRect:目标边界;
    • relativeRect:参照区域的边界; 

(4) 取消监听

当页面退出时记得要取消监听:io.disconnect()。

监听相交区域类

我们可以设计一个类,用来处理监听相交区域的逻辑。

构造函数

首先来看构造函数,代码如下:

class IntersectionObserver {
  constructor(options) {
    this.$options = {
      context: null,
      threshold: 0.5,
      initialRatio: 0,
      observeAll: false,
      selector: null,
      relativeTo: null,
      onEach: res => res.dataset,
      onFinal: () => null,
      delay: 200,    
      ...options,
    }
    this.$observer = null
  }
}

显然,构造函数传入了一些重要的参数,包括 createIntersectionObserver 所需要的三个参数:thresholds, initialRatio, observeAll 和上下文 context ;设置参考区域所需的 relativeTo ;监听方法所需的目标模块选择器 selector

最后还有 IntersectionObserver 类监听调用时需要的三个参数:

  • onEach:每一次触发监听调用时,也会调用 onEach 方法;
  • onFinal:在触发监听调用一段时间 delay 后,会调用一次 onFinal 方法。在模块曝光埋点场景下,如果页面在快速滚动时,每次的监听触发都上报埋点,一时间请求会堆积很多,所以需要 onFinal 方法,在一段时间后统一上报曝光埋点;
  • delay:调用 onFinal 方法的间隔时间;

监听

要想开始监听相交区域,需要先创建监听器,设置完相交区域后再开始监听,关键代码如下:

_createObserver() {
  const opt = this.$options
  const observerOptions = {
    thresholds: [opt.threshold],
    observeAll: opt.observeAll,
    initialRatio: opt.initialRatio,
  }

  // 创建监听器
  const ob = opt.context
    ? opt.context.createIntersectionObserver(observerOptions)
    : wx.createIntersectionObserver(null, observerOptions)

  // 相交区域设置
  if (opt.relativeTo) ob.relativeTo(opt.relativeTo)
  else ob.relativeToViewport()

  // 开始监听
  let finalData = []
  let isCollecting = false   
  ob.observe(opt.selector, res => {
    const { intersectionRatio } = res
    const visible = intersectionRatio >= opt.threshold
    if (!visible) return

    const data = opt.onEach(res)
    finalData.push(data)

    if (isCollecting) return    // 正在收集监听结果,不会调用 onFinal
    isCollecting = true    

    // 设置延迟调用 onFinal
    setTimeout(() => {
      opt.onFinal.call(null, finalData)
      finalData = []
      isCollecting = false
    }, opt.delay)
  })
  return ob
}

对外暴露的公用方法

封装对外的 _createObserver 方法:

connect() {
  if (this.$observer) return this
  this.$observer = this._createObserver()
  return this
}

封装停止监听的方法:

disconnect() {
  if (!this.$observer) return
  const ob = this.$observer
  if (ob.$timer) clearTimeout(ob.$timer)
  ob.disconnect()
  this.$observer = null
}

使用方法

import IntersectionObserver from './intersection-observer.js';

const ob = new IntersectionObserver({...})
ob.connect()

详见代码片段:developers.weixin.qq.com/s/lqUakfmM7… 

总结

当然,曝光埋点也可以使用传统 Web 的监听 scroll 事件的方式。不过,既然小程序提供了 IntersectionObserver API 并且几乎所有客户端都已支持,那自然就用这种更方便的方式。

另外,在百度小程序和支付宝小程序上也有支持相关的API,跨端开发也不用考虑其他小程序不支持。

关于兼容性

支付包小程序兼容性有待考证,百度可以使用 IntersectionObserver,不过需要注意 this.createIntersectionObserver 非组件是没有的,只能使用 swan.createIntersectionObserver(this);第二点,createIntersectionObserver的参数observeAll 需要改成 selectAll (百度小程序代码片段:swanide://fragment/142c0f60156b1e850dc239553ecffe7b1571810456384)。

参考文档

  1. 官方文档
  2. 谈谈IntersectionObserver懒加载(Web API 表现形式详解,和小程序上相同);