||

Vue使用自定义指令和混入器实现PV统计

最近在做的工业互联网项目需要对用户的一些行为数据进行统计,以便分析需求数据,其中重要的一项就是 PV 数据,本文将以介绍使用 Vue 的自定义指令来处理 PV 数据上报。

统计原理

PV 数据的统计原理还是很简单的,当用户点击某个组件后,我们就上报这次点击,所以其中的一个关键点,就在于需要对所有的组件添加一个 click 事件监听。

几年前在其他项目中也有类似的需求,当时是通过 JQuery 来绑定事件监听,但是难点在于你并不确定哪些页面部分是需要统计的,哪些是不需要统计的,所以一旦方案有变,就以为这要重写事件监听,这一点也不友好。

不过现在前端框架流程,基于 Vue 来做这件事就变的比较简单了,Vue 支持自定义指令,我们可以给需要监听的组件增加一个 自定义指令 ,然后当用户点击某个组件的时候通过自定义指定中的事件监听来触发。

亦或者使用 Vue 的混入器做全局混入,需要统计的数据的组件定义对应的属性即可,相比于自定义指令,全局混入可以支持更复杂的数据结构,也就可以做更多的事情,但弊端是全局混入使用时需要非常小心。

下面我将介绍这两种方案。

自定义指令

除了核心功能默认内置的指令 (v-modelv-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。

Vue 中的自定义指令通过 Vue.directive 来定义,例如我们需要定义一个 pvboss 指令,当某个 DOM 元素被设置了 v-pvboss 指令时,就未这个节点添加一个事件监听,如果此时用户点击了这个元素,那么就会触发一次 PV 上报。

来看代码:

// 定义 pvboss 指令
Vue.directive("pvboss", {
  // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  bind: function (el) {
    el.pvbossListerner = (e) => {
      console.log("post by v-pvboss", el);
      e.stopPropagation();
    }
    el.addEventListener("click", el.pvbossListerner);
  },
  // 只调用一次,指令与元素解绑时调用。
  unbind: function (el) {
    if (!el.pvbossListerner) return;
    el.removeEventListener("click", el.pvbossListerner);
    console.log("remove pv listener by v-pvboss");
  }
})

上面的代码,定义了一个 click 事件监听函数,这个函数被挂载到了元素的 $pvbossListerner 属性上,这个属性是我们自定义的,之所以定义这个属性,是为了在 unbind 钩子中可以方便的移除事件监听,如果使用匿名函数,那么当组件元素生命结束后,也可能无法移除简单,会影响性能。

晚上上面的指令定义后,就可以在对应的DOM元素上使用了,以组件中使用为例:

<template>
  <div class="home">
    <HelloWorld v-pvboss msg="hello world"/>
  </div>
</template>

当用户在 HelloWolrd 组件上触发点击时候时,其节点的 $pvbossListerner 就会被回调,我们只需在这个回调中定义数据上报即可。

这种方式的好处是我们不需要管组件内部的实现,不用改动组件内部的代码,只需要在使用组件的时候,按照需要来定义指令,携带有该指令的组件或者 DOM 元素就会被监听。

全局混入

上面自定义指令的确是一种方式,另外一种实现是通过 Vue 的 mixin 方法做全局混入。使用混入可以实现一些代码的复用,对于我们来说,统计业务 PV 的代码就是复用的,所以全局混入可以满足要求。

定义一个全局混入器:

Vue.mixin({
  mounted: function () {
    var pvboss = this.options.pvboss
    if (pvboss) {
      console.log(pvboss)
      this.pvbossListerner = (e) => {
        console.log("post by mixer", this.el);
        e.stopPropagation();
      }
      this.el.addEventListener("click", this.pvbossListerner);
    }
  },
  destroyed: function () {
    if (!this.options.pvboss) return;
    this.el.removeEventListener("click", this.pvbossListerner);
    console.log("remove pv listener by mixer")
  }
})

上文中的混入器在 mounted 和 destroyed 两个生命周期函数中做了处理,为了方便在组件销毁后移除监听,我们将 click 的实践回调绑定在了 this.$el 上,而 $el 属性需要在 mounted 钩子之后才能被访问到。

定义完混入器以后,我们只需在需要被统计的组件中增量一个 pvboss 属性即可,还是以 HelloWorld 组件为例:

export default {
  // 定义 pvboss 属性为 true
  pvboss: true,
  name: 'HelloWorld',
  props: {
    msg: String
  }
}

相比于自定义指令,使用混入的方式可以拥有更大的粒度,并以组件作为最小统计单位,这更符合常理。

总结

文中介绍了两种方式来实现数据上报,这两种方式都可以在不改变现有组件的情况下实现需求,确实分方便。不过这两种方式也并不是十全十美的,因为正常业务上报的数据类型非常多,不仅仅是 PV 这么简单,很多时候我们还需要对页面、展示等等各个维度进行采集和上报,不过即便如此,上面的方式也提供了思路。

对比之下,这两种方案的侧重点是不同的:

  • 自定义指令:粒度更小,不仅仅组件本身可以成为一个上报节点,组件内部的任意一个 DOM 节点都可以成为上报的节点,所以在设置不当的情况下会出现冗余;
  • 全局混入:粒度更大,这种方案只会以单个组件作为上报的最小单位,忽略 DOM 元素,相比于自定义指令其粒度较大。

我个人是推崇使用全局混入的,因为全局混入的粒度始终,统计的最小单位是组件,而往往在页面中组件意味着某一种独立的功能,这更符合业务数据的需要。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注