好玩的JS系列--小程序数据埋点

小编推荐:Fundebug专注于JavaScript、微信小程序、微信小游戏,Node.js和Java实时BUG监控。真的是一个很好用的bug监控费服务,众多大佬公司都在使用。

前言

  最近负责的一个项目,项目的客户端是微信小程序。客户提了需求,需要进行数据埋点,收集用户行为数据,为后续提升用户体验及产品优化提供基础数据收集。于是在一个风和日丽的日子,撸起袖子动手干。

思路

  先看看客户端常见的数据埋点方案如下图。

数据埋点方案.png


  侵入式埋点这个如果在页面少,数据收集需求变化不大的情况下,还是挺经济实惠的。鉴于当前项目页面较多,数据收集的需求变化也在不断的调整,所以侵入式埋点的方案不适用本项目,只有松耦合这个方案了。

小程序的特点

  根据松耦合埋点方案,首先要解决的问题是拦截小程序所有控件的操作数据。在Web中有BOM对象及DOM对象可以给我们进行全方位的操作,特别是BOM对象,可以进行全局事件拦截。而小程序类似BOM对象的主要有下面三个对象:App、Page、Component。

  • App:App对象有且只有一个,属于小程序的总控对象,App下面包含着n个Page对象。
  • Page:Page则通过字面理解就是页面的总控对象,一个小程序可以有多个Page,每一个Page对象对应一个页面,Page下面可以包含n个Component及其他普通的小程序组件。
  • Component:Component则是自定义组件对象,类似于Page,只明生命周期及一些特性上有所区别,自定义组件对象在实际项目中比较常用,主要用来解决小程序页面堆栈太小的问题,这个后续再写另外一篇来讲。

  小程序的事件机制也是采用与JavaScript一样的事件机制,捕获->执行->冒泡的机制,JavaScript在BOM及DOM对象中都提供了addEventListener这种订阅/发布机制,使我们可以轻松的拦截所有的事件,又不影响现有的流程及代码,从而实现松耦合埋点方案。但是小程序没有提供addEventListener的订阅/发布机制,没有办法通过这样子的方案来实现。

1.拦截器方式

翻了官网,发现没有办法统一的处理,那么只能退而求其次,减少对现有的代码的侵入,于是想到了拦截器的机制来实现。

  看看下面小程序原生的代码

App({
  onLaunch(options) {
    // Do something initial when launch.
  },
  onShow(options) {
    // Do something when show.
  },
  onHide() {
    // Do something when hide.
  },
  onError(msg) {
    console.log(msg)
  },
  globalData: 'I am global data'
})

  提供拦截器,这里只拿App的onLaunch事件进行示例,其他事件都差不多这样处理即可。

var appFilter = function(config){
  if(config.onLaunch){
    let _onLaunch = config.onLaunch;
    config.onLaunch = function(ops){
         //在这里干埋点的事
        //例如存储数据、上送数据
         _onLaunch.call(this);//调用原来的执行逻辑
    }
  }
  return config;
}


//App使用拦截器示例
App(appFilter({
  onLaunch(options) {
    // Do something initial when launch.
  },
  onShow(options) {
    // Do something when show.
  },
  onHide() {
    // Do something when hide.
  },
  onError(msg) {
    console.log(msg)
  },
  globalData: 'I am global data'
})
)

  这个也是JS好玩的地方,一切皆为对象。通过新增一个Function对象,增强方法之后,把旧的Function对象进行替换。上面的App的onLaunch方法,经过appFilter过滤器的处理后,就替换成过滤器里面加了埋点事件的新方法了,对于开发人员来讲,不需要去改动原来App.onLaunch事件里面的代码,目的达到了,但是不够完美,在写过滤器的实现逻辑时,给了我一个灵感,我是否可以用这种方式来实现不侵入代码的埋点?答案是可以的。

2.增强扩展方式

  利用新增一个Function对象,增强方法之后,把旧的Function对象进行替换这样的原理,依次将App、Page、Component这三个对象进行增强扩展,示例代码如下。

//先把原生的三个对象保存起来
const originalApp = App,
      originalPage = Page,
      originalComponent = Component;
//在原生的事件函数里面,添加数据埋点,并替换成新的事件函数
const _extendsApp = function (conf, method) {
  const _o_method = conf[method];
  conf[method] = function (ops) {
    //在此处进行数据埋点
    if (typeof _o_method === 'function') {
      _o_method.call(this, ops);
    }
  }
}

//重新定义App这个对象,将原来的App对象覆盖
App = function(conf){
  //定义需要增强的方法
  const methods = ['onLaunch', 'onShow', 'onHide', 'onError']

  methods.map(function (method) {
    _extendsApp(conf, method);
  })
  //另外增强扩展埋点上送的方法
  conf.william = {
    addActionData: function (ops) {
      console.log('addActionData');
    },
    addVisitLog: function (ops) {
      console.log('addVisitLog');
    }
  }
  return originalApp(conf);
}
//Page及Component对象类似App的处理即可

  至此,整个小程序的生命周期都在掌握之中了,可以按需采集对应的数据,并且对于开发人员来讲,还不需要去修改及调整代码,松耦合埋点方案搞定。

3.增强扩展+订阅/发布

  上面的实现方案虽然实现了松耦合,但是个人觉得还不够完美,埋点的动作必须要写在增加的方法里面,这样子可维护性较差,也不够灵活。解耦这事现在对我来说信手拈来,祭出了我的EventHub(基于订阅/发布模式实现的消息总线),完美。

//引用EventHub
import EventHub from '../../utils/eventhub.min';
//先把原生的三个对象保存起来
const originalApp = App,
      originalPage = Page,
      originalComponent = Component;
//在原生的事件函数里面,添加数据埋点,并替换成新的事件函数
const _extendsApp = function (conf, method) {
  const _o_method = conf[method];
  conf[method] = function (ops) {
    //在此处进行数据埋点
    //此处改成消息发布
    if (typeof EventHub != "undefined") {
      EventHub.emit('app' + method, ops);
    }
    if (typeof _o_method === 'function') {
      _o_method.call(this, ops);
    }
  }
}

//重新定义App这个对象,将原来的App对象覆盖
App = function(conf){
  //定义需要增强的方法
  const methods = ['onLaunch', 'onShow', 'onHide', 'onError']

  methods.map(function (method) {
    _extendsApp(conf, method);
  })
  //另外增强扩展埋点上送的方法
  conf.william = {
    addActionData: function (ops) {
      console.log('addActionData');
    },
    addVisitLog: function (ops) {
      console.log('addVisitLog');
    }
  }
  return originalApp(conf);
}
//Page及Component对象类似App的处理即可

  这样子就可以把埋点处理的逻辑抽离到另外一个JS文件中去实现。

EventHub.on('apponLaunch',function(ops){
    //在这里可以处理数据埋点的事
})

延展性思考

  基于上述的实现方案,我们除了增强生命周期,也可以像上面那样去增加公用的方法,例如App.william.addActionData,更可以通过增强setData方法来进行数据溯源或者进行差量比较来提升性能等方式。


作者:飙猪狂
链接:https://www.jianshu.com/p/3253a15707fc

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了9亿+错误事件,得到了Google、360、金山软件、百姓网等众多知名用户的认可。欢迎免费试用!

猜你喜欢

转载自blog.csdn.net/qq_40126542/article/details/85276414
今日推荐