微信小程序数据统计和错误统计的实现方法


Posted in Javascript onJune 26, 2019

某些情况下我们需要对小程序某些用户的行为进行数据进行统计,比如统计某个页面的UV, PV等,统计某个功能的使用情况等。好让产品对于产品的整个功能有所了解。 在网页里,我们很多人都用过谷歌统计,小程序里也有一些第三方数据统计的库, 比如腾讯的MTA等等。 但是,第三方的数据统计库要么功能太简单,满足不了需求,要么就是要收费。(留下了贫穷的泪水。) 等等,又不是你出钱,怕啥? 贵一点就贵一点呀。

嗯,说的没错。但是,公司团队内部想实现一套完整的自己的数据统计系统以满足自己的需求。所以,还是没有用第三方的。

所以,具体要统计些啥?

产品经理

  • 想知道用户都是怎么进入我们的小程序的?
  • 用户在我们小程序里那个页面停留的时间最长?平均用户停留时间是多少?
  • 想知道我们最近开发的那个功能用的人多不多?
  • 想统计小程序里的一些按钮有多少用户点击了

开发自己

  • 总是很难复现用户端出现的bug,
  • 要是可以知道用户端发生错误时,知道用户当时的用的手机型号,微信版本,网络环境,页面参数,和错误信息就好了
  • 想知道我们小程序启动时间是多少?
  • 接口在用户端的平均响应时间是多少ms? 哪些接口报错了

针对产品经理的需求,我们可以知道,Ta想要的是就是数据统计要实现的功能。对于开发来说,我们关注的更多就是错误统小程序性能这块的东西。

好,到这里,我们需求是明白了。就是要实现一套既能统计普通的埋点数据,也要能统计到小程序里一些特殊触发的事件,比如appLaunch, appHide 等,还要可以统计错误。

好,那先来看看如何实现产品的需求吧

用户进入小程序可以在 小程序 onLaunch 回调里拿到参数 的scene 值,这样就可以知道用户是怎么进入小程序的了。小case, 难不到我。

嗯,第一个需求实现了,那如何统计第二个呢?如何统计某个页面的停留时间呢?

这也难不倒我,用户在进入页面时会触发onShow 事件, 同样,在离开页面(或者切后台时)会触发onHide事件,我只需要在onShow里记录一下时间,同时在onHide 里也记录一下时间,把两个时间一减就可以了。

Page({
  data: {
  beginTime: 0,
  endTime: 0
  },
  onShow: function() {
   // Do something when page show.
   this.setData({
   beginTime: new Date().getTime()
   })
  },
  onHide: function() {
   // Do something when page hide.
   let stayTime = new Date().getTime() - this.beginTime;
   // 这个就是用户在这个页面的停留时间了
  },
 })

等等,这样确实实现了需求,万一产品要统计所有也面的停留时长? 那我们岂不要在每一个页面都这样写一遍?有没有更好的方法呢?

好,接下来就是数据统计实现的要点了,即拦截微信原生事件,这样可以在某个特殊事件触发时,做一些我们统计的事情。同时,还要拦截微信发生网络请求的方法,这样可以拿到网络请求相关的数据,最后,为了能统计到错误,还需要拦截微信发生错误的方法。

1.特殊事件的监听

App(Object object)

注册小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。

App() 必须在 app.js 中调用,必须调用且只能调用一次。不然会出现无法预期的后果。

  • 拦截全局的事件:
  • 下面是小程序官方文档对于App 注册方法的文档:
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'
})

假如我们要在小程序onLaunch 时打印一句hello Word,我们有哪些方法实现?

方法1:

直接写在onLaunch方法里

onLaunch (options) {
  console.log('hello World')
 }

方法2:

使用 monkey patch方法猴子补丁(monkey patch)

猴子补丁主要有以下几个用处:

  1. 在运行时替换方法、属性等
  2. 在不修改第三方代码的情况下增加原来不支持的功能
  3. 在运行时为内存中的对象增加patch而不是在磁盘的源代码中增加

举个栗子,假如我们在console.log 方法里都先打印出当前的时间戳,我们可以这样:

var oldLog = console.log
console.log = function() {
 oldLog.call(this, new Date().getTime())
 oldLog.apply(this, arguments)
}

同理,我们针对onLaunch 进行猴子补丁

var oldAp = App
App = function(options) {
 var oldOnLaunch = options.onLaunch
 options['onLaunch'] = function(t) {
 // 做一些我们自己想做的事情
 console.log('hello word....')
 // 调用原来的onLaunch 方法
 oldOnLaunch.call(this, t)
 }
 
 // 调用原来的App 方法
 oldApp(options)
 
 // 想像一下,小程序内部调用onLaunch 方法应该是这样子的:
 options.onLaunch(params)
}

// 问题,有的时候,我们可能没有注册某一个事件,比如页面的onShow, 所有,我们在替换的时候还需要判断一下参数是否传了对应的方法
Page({
 onLoad (options) {},
 onHide (options) {}
})

// 针对这种情况,我们需要这样写
var oldPage = Page
Page = function(options) {
 if (options['onShow']) {
 // 如过有注册onShow 这个回调
 var oldOnShow = options.onShow
 // onShow 方法调用时都是 传了一个对象
 options['onShow'] = function(t) {
  // doSomething()
  oldOnShow.call(this, t)
 }
 }
 // 调用原来的Page 方法。
 oldPage.apply(null, [].slice.call(arguments))
 // 注意: 下面这两种写都会报错: VM23356:1 Options is not object: {"0":{}} in pages/Badge.js 问题具体原因暂时未找到。
 // oldPage.call(null, arguments)
 // oldPage(arguments)
}

通过上面的方法,我们可以拦截了 App 方法注册的一些全局方法,比如 onLaunch , onShow, onHide, 和Page 注册的事件如 onShow, onHide, onLoad, onPullDownRefresh, 等页面注册事件。

2.网络请求的监听

思路: 拦截微信的请求事件。

let Request = {
  request: function (e) {
  let success = e[0].success,
   fail = e[0].fail,
   beginTime = smaUtils.getTime(),
   endTime = 0
  // 拦截请求成功方法
  e[0].success = function () {
   endTime = smaUtils.getTime()
   const performance = {
   type: constMap.performance,
   event: eventMap.wxRequest,
   url: e[0].url,
   status: arguments[0].statusCode,
   begin: beginTime,
   end: endTime,
   total: endTime - beginTime
   }
   smaUtils.logInfo('success performance:', performance)
   // 这里做上报的事情
   // SMA.performanceReport(performance)
   success && success.apply(this, [].slice.call(arguments))
  }
  // 拦截请求失败方法
  e[0].fail = function () {
   endTime = smaUtils.getTime()
   const performance = {
   type: constMap.performance,
   event: eventMap.wxRequest,
   url: e[0].url,
   status: arguments[0].statusCode,
   begin: beginTime,
   end: endTime,
   total: endTime - beginTime
   }
   smaUtils.logInfo('fail performance:', performance)
   // 这里做上报的事情
   // SMA.performanceReport(performance)
   fail && fail.apply(this, [].slice.call(arguments))
  }
  },
 }
 
 
 // 替换微信相关属性
 let oldWx = wx,
  newWx = {}
 for (var p in wx) {
  if (Request[p]) {
  let p2 = p.toString()
  newWx[p2] = function () {
   Request[p2](arguments)
   // 调用原来的wx.request 方法
   oldWx[p2].apply(oldWx, [].slice.call(arguments))
  }
  } else {
  newWx[p] = oldWx[p]
  }
 }
 // eslint-disable-next-line
 wx = newWx

疑惑:为什么要使用替换整个wx对象的方法呢? 不直接用我们的request 方法 替换 wx.request 方法

var oldRequest = wx.request
wx.request = function(e) {
 // doSomething();
 console.log('请求拦截操作...')
 oldRequest.call(this, e); // 调用老的request方法
}
// 结果报错了:
// TypeError: Cannot set property request of [object Object] which has only a getter

3.错误的监听

3.1 拦截App里注册的 onError事件

var oldAp = App
App = function(options) {
 var oldOnError = options.onErrr
 options['onErrr'] = function(t) {
 // 做一些我们自己想做的事情
 console.log('统计错误....', t)
 // 调用原来的onLaunch 方法
 oldOnError.call(this, t)
 }
 
 // 调用原来的App 方法
 oldApp(options)
}

3.2 拦截 conole.error

console.error = function() {
  var e = [].slice.call(arguments)
  if (!e.length) { return true }
  const currRoute = smaUtils.getPagePath()
  // 统计错误事件
  // SMA.errorReport({event: eventMap.onError, route: currRoute, errrMsg: arguments[0]})
  smaUtils.logInfo('捕捉到error 事件,', e)
  oldError.apply(console, e)
 }

至此,我们已经有能力在小程序发起请求时,发生错误时,生命周期或者特殊函数回调时,我们都能在里面做一些我们想要的数据统计功能了。

说了这么多大家估计也看累了。鉴于篇幅,具体的代码就不在这里贴了。

最终实现的数据统计模块大致实现了以下功能:

  • 普通埋点信息上报功能
  • 错误信息上报功能
  • 性能数据上报功能
  • 具体的上报时机支持配置
  • 支持指定网络环境上报
  • 支持统计数据缓存到微信本地功能

整个统计代码的配置文件如下:

const wxaConfig = {
 project: 'myMiniProgram', // 项目名称
 trackUrl: 'https://youhost.com/batch', // 后台数据统计接口
 errorUrl: 'https://youhost.com/batch', // 后台错误上报接口
 performanceUrl: 'https://youhost.com/batch', // 后台性能上报接口
 version: '0.1',
 prefix: '_wxa_',
 priority: ['track', 'performance', 'error'], // 发送请求的优先级,发送时,会依次发送
 useStorage: true, // 是否开启storage缓存
 debug: false, // 是否开启调试(显示log)
 autoTrack: true, // 自动上报 onShow, onHide, 分享等 内置事件
 errorReport: false, // 是否开启错误上报
 performanceReport: false, // 接口性能上报
 maxReportNum: 20, // 当次上报最大条数
 intervalTime: 15, // 定时上报的时间间隔,单位 s, 仅当开启了定时上报有效。
 networkList: ['wifi', '4g', '3g'], // 允许上报的网络环境
 opportunity: 'pageHide' // pageHide、appHide、realTime(实时上报)、timing(定时上报) 上报的时机,四选一
}
export default wxaConfig

具体上报时,上报的数据结构大致长这样:

微信小程序数据统计和错误统计的实现方法

项目已传到GitHub -> GitHub传送门-wxa

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript json 新手入门文档
Dec 03 Javascript
javascript在子页面中函数无法调试问题解决方法
Jan 17 Javascript
JS解析XML文件和XML字符串详解
Apr 17 Javascript
js实现按钮颜色渐变动画效果
Aug 20 Javascript
使用jQuery的ajax方法向服务器发出get和post请求的方法
Jan 13 Javascript
通过button将form表单的数据提交到action层的实例
Sep 08 Javascript
使用vuex缓存数据并优化自己的vuex-cache
May 30 Javascript
JavaScript如何获取一个元素的样式信息
Jul 29 Javascript
微信小程序wx.navigateTo方法里的events参数使用详情及场景
Jan 07 Javascript
JS快速实现简单计算器
Apr 08 Javascript
js实现QQ邮箱邮件拖拽删除功能
Aug 27 Javascript
Python机器学习之决策树和随机森林
Jul 15 Javascript
Vue移动端右滑屏幕返回上一页附源码下载
Jun 26 #Javascript
微信小程序利用Canvas绘制图片和竖排文字详解
Jun 25 #Javascript
js+html实现周岁年龄计算器
Jun 25 #Javascript
Vue 列表上下过渡效果的实例代码
Jun 25 #Javascript
JS 封装父页面子页面交互接口的实例代码
Jun 25 #Javascript
微信小程序Echarts图表组件使用方法详解
Jun 25 #Javascript
ES6 Object方法扩展的应用实例分析
Jun 25 #Javascript
You might like
星际中的相关伤害
2020/03/04 星际争霸
php操作excel文件 基于phpexcel
2010/07/02 PHP
PHP实现生成模糊图片的方法示例
2017/12/21 PHP
php+mysql开发中的经验与常识小结
2019/03/25 PHP
jquery js 获取时间差、时间格式具体代码
2013/06/05 Javascript
javascript嵌套函数和在函数内调用外部函数的区别分析
2016/01/31 Javascript
jquery解析XML及获取XML节点名称的实现代码
2016/05/18 Javascript
js防阻塞加载的实现方法
2016/09/09 Javascript
JavaScript实现的选择排序算法实例分析
2017/04/14 Javascript
基于vue 添加axios组件,解决post传参数为null的问题
2018/03/05 Javascript
Vue2.0 事件的广播与接收(观察者模式)
2018/03/14 Javascript
Vue实现base64编码图片间的切换功能
2019/12/04 Javascript
简单上手Python中装饰器的使用
2015/07/12 Python
Python3.7实现中控考勤机自动连接
2018/08/28 Python
Python3.4 splinter(模拟填写表单)使用方法
2018/10/13 Python
Python编程中flask的简介与简单使用
2018/12/28 Python
python实现多层感知器MLP(基于双月数据集)
2019/01/18 Python
Python使用python-docx读写word文档
2019/08/26 Python
pygame实现烟雨蒙蒙下彩虹雨
2019/11/11 Python
Python使用docx模块实现刷题功能代码
2020/02/13 Python
vivo智能手机官方商城:vivo
2016/09/22 全球购物
阿迪达斯奥地利官方商城:adidas.at
2016/10/16 全球购物
新加坡领先的时尚生活方式零售品牌:CHARLES & KEITH
2018/01/16 全球购物
仓管岗位职责范本
2014/02/08 职场文书
《手指教学》反思
2014/02/14 职场文书
实习评语大全
2014/04/26 职场文书
2014党员批评和自我批评思想汇报
2014/09/21 职场文书
升职自我推荐信范文
2015/03/25 职场文书
2015年党务公开工作总结
2015/05/19 职场文书
职工食堂管理制度
2015/08/06 职场文书
3招让你摆脱即兴讲话冷场尴尬
2019/08/08 职场文书
详解Vue的sync修饰符
2021/05/15 Vue.js
python模块与C和C++动态库相互调用实现过程示例
2021/11/02 Python
JavaScript实现酷炫的鼠标拖尾特效
2022/02/18 Javascript
Python数据处理的三个实用技巧分享
2022/04/01 Python
阿里云ECS云服务器快照的概念以及如何使用
2022/04/21 Servers