Intersection Observer API 实现数据埋点 批量曝光
基于NuxtJS 2.x
构建的项目实现
官网介绍:MDN Web Docs 之 Intersection Observer API
1、
new IntersectionObserver()
实例化一个全局_observer
,每个DOM
节点自行把自己加入_observer
的观察列表 (此处会用Vue
的指令来实现)2、当某个
DOM
节点进入视窗,收集该DOM
的信息,存进一个全局数组dotArr
中,然后取消对该DOM
的观察3、从
dotArr
中取数据上传跑定时器,每隔 N 秒检查一次,如果
dotArr
有数据,就直接上报;如果 N 秒内,
dotArr
的数据量大于某个量maxNum
,不等定时器,直接全部上报4、不漏以及不重复上报数据,用户离开页面前的边界数据处理
浏览器环境:
dotArr
同时存一份在localStorage
中,同步更新数据(增加或者上报完后清空),如果用户真的在 N 秒的间隔内,而数据又不够最大上报量maxNum
就离开了页面,那么这批数据就等用户下次再进页面时,直接从localStorage
中取出来上传。当然如果这个用户再也不进页面或者清空了浏览器缓存,这一点点数据丢失是可以接受。
曝光监听
创建
intersection-observer.js
文件
// 安装 intersection-observer 插件 npm install intersection-observer --save-dev // 创建观察文件 intersection-observer.js import "intersection-observer"; import axios from "axios"; // 数据上报方法,即网络请求 const platformExposure = (dotDataArr) => { axios .post("/api/xxx/exposure", { para: { list: dotDataArr } }) }; // 节流的时间,默认是100ms IntersectionObserver.prototype.THROTTLE_TIMEOUT = 300; const localStorage = window.localStorage; export default class Exposure { constructor(maxNum = 200) { this.dotDataArr = []; // 进入视窗的DOM节点的数据 this.maxNum = maxNum; this.timeout = 1 * 1000 * 60; // 间隔时间上传一次 this._timer = 0; this._observer = null; // 观察者的集合 this.init(); // 全局只会实例化一次Exposure类 } init() { const self = this; // init只会执行一次,边界处理方法,把浏览器localStorage里面的剩余数据上传 this.dotFromLocalStorage(); this._observer = new IntersectionObserver( (entries, observer) => { // 每一个产品进入视窗时都会触发 entries.forEach((entry) => { if (entry.isIntersecting) { // 清除当前定时器 clearTimeout(self._timer); // 把相关的数据直接放DOM上面了,比如 <div :data-dot="哈哈" ></div> // const ctm = entry.target.attributes['data-dot'].value const dataset = entry.target.dataset; const ctm = { platform_id: dataset.id, // 产品id 必填 }; // 收集数据,进待上报的数据数组 self.dotDataArr.push(ctm); // 收集到数据后,取消对该DOM节点的观察 self._observer.unobserve(entry.target); // 超过一定数量直接上传 if (self.dotDataArr.length >= self.maxNum) { self.dot(); } else { // 否则,直接缓存 self.storeIntoLocalstorage(self.dotDataArr); if (self.dotDataArr.length > 0) { // 不断有新的ctm进来,接下来如果没增加,自动n秒后打点 self._timer = window.setTimeout(() => { self.dot(); }, self.timeout); } } } }); }, { root: null, // 指定根目录,也就是当目标元素显示在这个元素中时会触发监控回调。 默认值为null,即浏览器窗口 rootMargin: "0px", // 设定root元素的边框区域 threshold: 0.5, // number或number数组,控制target元素进入root元素中可见性超过的阙值,达到阈值会触发函数,也可以使用数据来让元素在进入时在不同的可见度返回多次值 } ); } // 每个DOM元素通过全局唯一的Exposure的实例来执行该add方法,将自己添加进观察者中 add(entry) { this._observer && this._observer.observe(entry.el); } // 上传并更新缓存 dot() { const dotDataArr = this.dotDataArr.splice(0, this.maxNum); platformExposure(dotDataArr); this.storeIntoLocalstorage(this.dotDataArr); } // 缓存数据 storeIntoLocalstorage(dotDataArr) { localStorage.setItem("dotDataArr", JSON.stringify(dotDataArr)); } // 上传数据 dotFromLocalStorage() { const ctmsStr = JSON.parse(localStorage.getItem("dotDataArr")); if (ctmsStr && ctmsStr.length > 0) { platformExposure(ctmsStr); } } } 复制代码
曝光指令
完成曝光指令文件
directives.client.js
import Vue from "vue"; import Exposure from "./intersection-observer"; // exp 全局唯一的实例 const exp = new Exposure(); Vue.directive("exp-dot", { bind(el, binding, vnode) { // 每个使用了该指令的商品都会自动add自身进观察者中 exp.add({ el, val: binding.value }); }, update(newValue, oldValue) { // 值更新时的工作 // 也会以初始值为参数调用一次, 此时可以根据传值类型来进行相应埋点行为的请求处理 }, unbind() { // 清理工作 }, }); 复制代码
在配置文件
nuxt.config.js
中引入指令文件directives.client.js
,其中client
代表只在客户端生效
// nuxt.config.js module.exports = { mode: "universal", plugins: [{ src: "~plugins/directives.client.js" }], }; 复制代码
实战使用
核心使用就是v-exp-dot
<template> <div class="mescroll"> <div class="list-product"> <div class="list-item" v-for="(item, index) in listData" :key="item.id" :item="item" :data-id="item.id" :data-url="item.url" v-exp-dot /> </div> </div> </template>
作者:时光足迹
链接:https://juejin.cn/post/7018430369321975822