基于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插件npminstallintersection-observer--save-dev//创建观察文件intersection-observer.jsimport"intersection-observer";importaxiosfrom"axios";//数据上报方法,即网络请求constplatformExposure=(dotDataArr)=>{axios.post("/api/xxx/exposure",{para:{list:dotDataArr}})};//节流的时间,默认是100msIntersectionObserver.prototype.THROTTLE_TIMEOUT=300;constlocalStorage=window.localStorage;exportdefaultclassExposure{constructor(maxNum=200){this.dotDataArr=[];//进入视窗的DOM节点的数据this.maxNum=maxNum;this.timeout=1*1000*60;//间隔时间上传一次this._timer=0;this._observer=null;//观察者的集合this.init();//全局只会实例化一次Exposure类}init(){constself=this;//init只会执行一次,边界处理方法,把浏览器localStorage里面的剩余数据上传this.dotFromLocalStorage();this._observer=newIntersectionObserver((entries,observer)=>{//每一个产品进入视窗时都会触发entries.forEach((entry)=>{if(entry.isIntersecting){//清除当前定时器clearTimeout(self._timer);//把相关的数据直接放DOM上面了,比如<div:data-dot="哈哈"></div>//constctm=entry.target.attributes['data-dot'].valueconstdataset=entry.target.dataset;constctm={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(){constdotDataArr=this.dotDataArr.splice(0,this.maxNum);platformExposure(dotDataArr);this.storeIntoLocalstorage(this.dotDataArr);}//缓存数据storeIntoLocalstorage(dotDataArr){localStorage.setItem("dotDataArr",JSON.stringify(dotDataArr));}//上传数据dotFromLocalStorage(){constctmsStr=JSON.parse(localStorage.getItem("dotDataArr"));if(ctmsStr&&ctmsStr.length>0){platformExposure(ctmsStr);}}}
曝光指令
完成曝光指令文件 directives.client.js
importVuefrom"vue";importExposurefrom"./intersection-observer";//exp全局唯一的实例constexp=newExposure();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.jsmodule.exports={mode:"universal",plugins:[{src:"~plugins/directives.client.js"}],};
实战使用
核心使用就是v-exp-dot
<template><divclass="mescroll"><divclass="list-product"><divclass="list-item"v-for="(item,index)inlistData":key="item.id":item="item":data-id="item.id":data-url="item.url"v-exp-dot/></div></div></template>