当前位置: 首页 > Web前端 > HTML5

构建可视化用户行为轨迹管理系统SDK

时间:2023-04-06 00:06:10 HTML5

一、基本原理●1、利用xpath的唯一性绑定点元素,添加事件发送数据点●2、后台管理系统构建可视化选择点元素功能并保存配置●3、前端根据页面的URL获取dot配置并初始化(通过xpath绑定事件)。使用传统ajax请求发送数据的缺点是容易阻塞请求,对用户不友好,弊端很大。当用户关闭页面时,请求将被截断,即终止发送,不适用于记录浏览时间。axios.post(网址,数据);//以axios为例2.对于动态图片,我们可以通过在beforeunload事件处理函数中创建一个图片元素,并设置其src属性来保证数据的发送,从而实现延迟卸载,因为大多数浏览器的处理函数都会延迟卸载,以确保图像已加载,因此可以在卸载事件中发送数据。constsendLog=(url,data)=>{让img=document.createElement('img');常量参数=[];Object.keys(data).forEach((key)=>{params.push(`${key}=${encodeURIComponent(data[key])}`);});img.onload=()=>img=null;img.src=`${url}?${params.join('&')}`;};3.sendBeacon为了解决上述问题,有一个navigator.sendBeacon方法。使用这种方式发送请求可以保证数据的有效传递,不会阻塞页面的卸载或加载,而且编码比上面的方式更简单。exportconstsendBeacon=(url,analyticsData)=>{constapiUrl=config.apiRoot+urlletdata=getParams(analyticsData)//兼容不支持sendBeacon的浏览器if(!navigator.sendBeacon){constclient=newXMLHttpRequest()//第三个参数表示同步发送client.open('POST',apiUrl,false)client.setRequestHeader('Content-Type','application/x-www-form-urlencoded')client.send(data)return}constformData=newFormData()Object.keys(analyticsData).forEach((key)=>{letvalue=analyticsData[key]if(typeofvalue!=='string'){//formData只能追加字符串或者Blobvalue=JSON.stringify(value)}formData.append(key,value)})navigator.sendBeacon(apiUrl,formData)}最后我们使用动态图片的方式,因为阿里云提供的阿里云-collection-通过WebTracking收集日志来应对大量的数据采集,不会对网站本身的服务器造成压力。3.构建SDK使用webpack构建项目,打包单个sdk的js文件包,将sdk导入到前端(这部分就不细说了,有兴趣的可以搜索webpack的相关资料)写一个带有webpack的简单JSSDK●可视化选择xpath-reference插件SDK主要功能:暴力暴露初始化方法和打点方法(为了支持手动打点)添加选择xpath的功能,暴露给后台管理系统使用链接URL读取打点配置列表初始化绑定打点事件函数进入页面即可recordadotrecordBrowsingtimeSDK与父iframe的通信函数(为了向后台管理系统传递数据)记录游览时长示例://统计时长constviewTime=(data)=>{letstartTime=newDate().getTime()//浏览开始时间letendTime=null//浏览结束时间//页面卸载触发window.addEventListener('unload',()=>{endTime=newDate().getTime()letparams={viewTime:(endTime-startTime)/1000,eventType:'view',accessId:ACCESS_ID}params=Object.assign(params,data)sendLog(params)},false)}//选择xpath跨域跨页communicationimportPostmatefrom'postmate'importInspectorfrom'../plugins/inspect'//选择xpath节点插件letchildIframe=nullconstmyInspect=newInspector()constgetXpathForm=function(options){myInspect.setOptions(options,(data)=>{letparams={xpath:data,route:window.QWK_ANALYSIS_SDK_OPTIONS?.route||''}childIframe.emit('send-data-event',params)})}exportdefault{//与父iframe通信initMessage(){//在开发模式下启用节点选择调试if(process.env.BUILD_ENV==='dev'){document.querySelector('#selected').onclick=()=>{myInspect.setOptions({deactivate:true},(data)=>{console.log(data)})}}consthandshake=newPostmate.Model({//iframeparentgetsxpathgetXpath:(options)=>{getXpathForm(options)},//删除选择deactivate:()=>{myInspect.deactivate()}})//当父级<->子级握手完成时,可能会向父级握手发送事件.then((child)=>{childIframe=child})}}//导出SDK//main.js入口文件import{init}from'./lib/init'importactionfrom'./lib/action'importselectXpathfrom'./lib/select-xpath'//import{documentReady}from'./plugins/common'//初始化选择xpath的跨域通信selectXpath.initMessage()//documentReady(()=>{////初始化////init().then(res=>res)//})//导出SDK模块export{init,action}4.后台管理系统搭建xpath可视化选择第一步:第三方网站引入SDK到在sdk中写一个选择xpath的函数,暴露出来调用后台管理系统●可视化选择xpath-reference插件Step2:搭建管理系统搭建iframe加载网站,如图:这里需要调用SDK中选择网站xpath函数的方法,必须和加载的iframe中的网站通信因为是iframe加载的第三方网站,会存在跨域问题,所以我们需要一个插件来实现这个功能:'WebIframe',props:{src:{type:String,default:''},options:{type:Object,default:()=>({})}},data(){return{$child:空}},莫unted(){this.$nextTick(()=>this.initMessage())},方法:{initMessage(){lethandshake=newPostmate({container:this.$refs.content,url:this.src,name:'web-iframe-name',classListArray:['iframe-class-content'],})handshake.then((child)=>{this.$child=childchild.on('发送数据事件',(data)=>{this.$emit('selected',data)})})},//获取选择取xpathgetXpath(){letoptions={clipboard:false,//是否自动复制deactivate:true,//选择之后销售损坏...this.options}try{this.$child.call('getXpath',options)}catch(e){this.$errMsg('加载SDK失败,请重新加载网站试试~可能当前网站没有引入用户行为轨迹跟踪SDK,请联系相关人员添加')}},//去掉选中的pop-uplayerremove(){this.$child&&this.$child.call('deactivate')}}}}m.eventType!=='visible')letviewList=data.filter((m)=>m.eventType==='visible')letdynamicList=[]//动态生成节点//点击事件或其他事件eventList.forEach((item)=>{constnode=item.xpath&&document.evaluate(item.xpath,document).iterateNext()if(!node){//找不到节点,说明可能是一个动态生成的节点dynamicList.push(item)return}node.addEventListener(item.eventType||'click',()=>{action.track(item)})})//通过点击查找动态生成的节点文档让dynamicClickList=dynamicList.filter((m)=>m.eventType==='click')if(dynamicClickList&&dynamicClickList.length){document.onclick=(event)=>{consttarget=event.target||window.event.targetconstparentNode=target.parentNodefor(letitemofdynamicClickList){//首先检查找到的节点保存item.node=document.evaluate(item.xpath,document).iterateNext()||item.node}letxpathItem=dynamicClickList.find((m)=>{returnm.xpath&&(target===m.node||parentNode===m.node)})//找到节点,发送点xpathItem&&deletexpathItem.node&&action.track(xpathItem)}}4.目标进入可见区域使用场景:当目标进入用户可见区域时需要标记一些水平滚动切换元素,所以就有这样的需求IntersectionObserver参考文档链接letobserver=null//可见区域letisTrackList=[]//已经标记if('IntersectionObserver'inwindow){//创建一个监控节点可见区域observer=newIntersectionObserver(entries=>{constimage=entries[0]//进入可见区域if(image.isIntersecting){//当前可见区域的点配置letcurrent=viewList.find((m)=>m.xpath&&image.target===document.evaluate(m.xpath,document).iterateNext())//已经点缀lettrackEd=isTrackList.find((m)=>m.id===current.id)//不要命中已经命中的点if(current&&!trackEd){isTrackList.push(current)action.track(current)}}})}viewList.forEach((item)=>{constnode=item.xpath&&document.evaluate(item.xpath,document).iterateNext()if(!node){return}//监听节点observer.observe(node)})