在上一篇文章《巧用域名发散,缓解单广告位并发请求限制》中,我们讨论了如何将广告数据请求转化为单广告位的请求,并由此推断曝光统计也应该采用相同的处理方式。本文将进一步探讨如何通过前端技术实现这一目标,同时分析其对用户体验和网站设计的影响。
pv是什么?
pv(page view)是指页面浏览量,是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标。它是评价网站流量最常用的指标之一,简称为PV。
曝光是什么,区别在哪?
曝光关注的是用户在浏览器中看到了我们关注的东西后,给后台的一个反馈。与pv不同,曝光关注的是我页面中内容是否被用户统计到,换个说法是面向DOM结构的浏览量统计。
原来是什么样的,我想要的是什么样的?
原来的曝光统计就是很普通的加载后发出一个请求。虽然每个广告位都有自己的曝光,但这种曝光仅仅是能统计到今天投放的广告的量。这种方式没有太大实际意义,还白白占用带宽。我们希望统计的是用户看到的广告是哪些,因为用户不一定会看完整个页面,所以每个广告位的浏览量一定是小于等于pv。这样我们就可以统计到哪些广告位被砍的多,哪些广告位被看到的次数少,甚至哪些广告位从未被看到过。更进一步来说,曝光统计还可以进一步对页面设计以及改版有一定的知道意义,因为我们常说没有数据的优化就是空谈。对于大多数网站的页面往往都是在摸索中前进,我们并不知道什么样的设计更能够受到用户的青睐。
那么我们是如何知道大多数用户的想法呢?
我们可以让用户不知道自己正被调查,通过观察用户在浏览页面时的行为来统计判断用户的感受。嘴上往往会说谎,但身体却很诚实。用户在看网页时的交互行为往往是个人感受最客观的表达。
前端技术实现思路:
为了实现上述目标,我们需要使用前端技术来实现。具体来说,我们可以利用JavaScript来监听用户的浏览行为,收集相关的数据。例如,我们可以添加事件监听器来跟踪用户在页面上的点击、滚动等操作。此外,我们还可以利用AJAX技术来异步获取数据,避免阻塞主线程。最后,我们将收集到的数据进行分析和统计,生成相应的报告。
总的来说,通过前端技术实现曝光统计,不仅可以提高数据的准确性和可靠性,还可以帮助我们更好地了解用户需求和喜好,从而优化网站设计和内容布局。
首先,我们需要分析哪些用户行为是我们应该且可收集的。例如,页面浏览量(PV)、曝光、点击等是比较公认的用户行为,它们之间应该符合漏斗转化模型。此外,一些公司还会针对鼠标滑过、悬停时间等交互行为甚至行为细节(如页面渲染完成后的曝光时间、划过次数、悬停时长)进行统计。今天我们将简单介绍一下页面PV、曝光和点击中的曝光。
页面PV,通常通过JS发出一个请求,最简单的方法是利用标签的src属性发起GET请求。
点击分为两种情况:本身有跳转和本身无跳转。如果本身有跳转,一般的方式是统计链接+redirect原目标链接的方式。如果是本身无跳转,就需要通过JS绑定click事件,在事件回调函数中利用pv的方法发出一个个体请求,所以我们通常会把发pv的方法封装到公用模块。
接下来是曝光,顾名思义就是让用户看到才算。换个说法比较直接,就是相关的DOM出现在浏览器的可视区。大多数的用户端网页都是纵向滚动条的形式,判断是否出现在可视区,也变成了dom在文档中位置和页面滚动条位置的比较。
DOM位置 <= (滚动条位置 + 可视区高度)
当然,在我在这个需求的不断优化过程中经历了几个阶段:
阶段一:监听滚动事件和遍历DOM
这种思路主要来自于懒加载的实现,对页面中的相关DOM添加已定义的属性,每次触发页面的滚动事件,就遍历带有自定义属性的DOM,并比对DOM和滚动条的位置。如果DOM位置小于等于滚动条+可视区高度,我们就认为“曝光了”,这时我们就会利用pv的公共函数发出请求,后台接受请求,并计入统计数据库。对于发出过曝光请求的DOM,我们应该做出标记,以便下一次触发页面滚动时过滤掉。比如将ad-pv的属性值赋值为空(ad-pv=“”)。下一次不处理为空的DOM。
阶段二:减少频繁的调用
在处理滚动条事件时,我们常常遇到一个问题:频繁的回调和DOM状态修改导致性能损耗过大。例如,当用户滚动鼠标滚轮时,会触发多次的scroll事件回调。这主要是因为我们需要在每次鼠标滚轮动作后都执行一些操作,如更新样式等。为了解决这个问题,我们可以采用一种叫做“函数节流”或“防反跳”的技术,来减少不必要的回调调用次数。
具体来说,“函数节流”是一种限制函数执行频率的方法,它通过设置一个延迟时间(如500毫秒),确保函数在一个特定的时间窗口内只被调用一次。这样可以避免因频繁触发回调而造成的性能问题。
接下来,我们讨论如何减少DOM遍历的注册机制与引用计数控制状态的问题。在处理DOM时,我们可以通过使用数据结构来模拟一层DOM,以优化性能。具体来说,我们可以创建一个对象数组来存储所有需要曝光的DOM文档的位置信息,并按照位置从小到大进行排序。这样,每当触发滚动事件时,只需比较当前滚动条位置与数组中每一项的文档位置,符合位置区间则发出曝光请求,直到不符合位置区间为止。
这种方法的好处包括:
- 遍历数组比遍历DOM更快,且不需要在DOM上添加过多的标记;
- 不必每次都对所有的DOM进行比较,可以尽早停止;
- 对于已经曝光过的对象,可以从数组中删除,下一次处理函数可以直接从上一次停止的对象(文档位置对应的数据)开始比较。 关于如何生成这个数组,我建议不要一开始就遍历一遍DOM,特别是在异步加载渲染的页面或者使用了类似bigPipe的技术的情况下。在这种情况下,广告代码需要支持动态添加。因此,我们可以通过暴露一个用于添加曝光对象的接口,来实现动态添加功能。每次添加后,对数组按照文档位置进行排序,以确保事件处理时使用的是有序数组。这种动态添加的方式被称为注册机制。 目前,我对外暴露的接口仅包括添加接口。然而,绑定事件确实会带来一定的性能损耗,我不能总是在监听。理想的方式是,有曝光对象就监听,没有曝光对象就解绑事件。幸运的是,我的数组是“注册”进来的,我可以在注册时增加引用计数,发曝光请求的时候减少引用计数。这样,我就可以在注册(0->1)或是每次处理结束的时候判断是绑定还是不绑定事件。这样,我对外的接口还是只有注册用的函数。 这里介绍一个小技巧。解绑的时候,万一页面本身就对scroll绑定了事件,我这一解绑不就把页面的事件给解除了吗?好在jQuery提供了一个事件命名空间的概念。我也是只绑定和解除自己空间下的scroll。我绑定的空间名:ads-lazy-pv。 总结: 这样一个曝光的逻辑就开发完了。如果仅仅完成“任务”,其实并不难,但优化却占用了我很大的精力。这个曝光看起来没用太多高深的技术,但开发起来却很是用心。尤其是对于广告代码这种跑在所有页面上的代码,更要考虑到很多复杂的情况,稍有纰漏将是公司财产的损失。身为一个开发人员,每一步都要胆大心细。