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


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 相关文章推荐
获取页面高度,窗口高度,滚动条高度等参数值getPageSize,getPageScroll
Sep 22 Javascript
JS限制Textarea文本域字符个数的具体实现
Aug 02 Javascript
js window.open弹出新的网页窗口
Jan 16 Javascript
JS实现京东首页之页面顶部、Logo和搜索框功能
Jan 12 Javascript
Bootstrap媒体对象学习使用
Mar 07 Javascript
详解Vue2.X的路由管理记录之 钩子函数(切割流水线)
May 02 Javascript
Js实现中国公民身份证号码有效性验证实例代码
May 03 Javascript
微信小程序实现根据字母选择城市功能
Aug 16 Javascript
详解基于vue-router的动态权限控制实现方案
Sep 28 Javascript
在vue项目中,使用axios跨域处理
Mar 07 Javascript
手把手教你vue-cli单页到多页应用的方法
May 31 Javascript
vue + node如何通过一个Txt文件批量生成MP3并压缩成Zip
Jun 02 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
PHP二进制与字符串之间的相互转换教程
2016/10/14 PHP
PHP使用file_get_contents发送http请求功能简单示例
2018/04/29 PHP
Laravel框架处理用户的请求操作详解
2019/12/20 PHP
jQuery技巧大放送 学习jquery的朋友可以看下
2009/10/14 Javascript
子窗口、父窗口和Silverlight之间的相互调用
2010/08/16 Javascript
javascript 使用 NodeList需要注意的问题
2013/03/04 Javascript
jQuery实现多按钮单击变色
2014/11/27 Javascript
PHPMyAdmin导入时提示文件大小超出PHP限制的解决方法
2015/03/30 Javascript
全面解析JavaScript中apply和call以及bind(推荐)
2016/06/15 Javascript
js获取上传文件的绝对路径实现方法
2016/08/02 Javascript
关于Javascript回调函数的一个妙用
2016/08/29 Javascript
详解angular笔记路由之angular-router
2017/09/12 Javascript
谈谈JS中的!!
2017/12/07 Javascript
Vue用v-for给src属性赋值的方法
2018/03/03 Javascript
Vue中的vue-resource示例详解
2018/11/02 Javascript
微信小程序swiper禁止用户手动滑动代码实例
2019/08/23 Javascript
javascript设计模式 ? 简单工厂模式原理与应用实例分析
2020/04/09 Javascript
JS实现按比例缩小图片宽高
2020/08/24 Javascript
JavaScript 常见的继承方式汇总
2020/09/17 Javascript
在antd Form表单中select设置初始值操作
2020/11/02 Javascript
如何运行Python程序的方法
2013/04/21 Python
从局部变量和全局变量开始全面解析Python中变量的作用域
2016/06/16 Python
python 生成器生成杨辉三角的方法(必看)
2017/04/10 Python
使用DataFrame删除行和列的实例讲解
2018/04/08 Python
详细介绍Python进度条tqdm的使用
2019/07/31 Python
Python基础之字典常见操作经典实例详解
2020/02/26 Python
雪花秀美国官方网站:韩国著名草本护肤化妆品品牌
2016/10/19 全球购物
药物学专业学生的自我评价
2013/10/27 职场文书
生产车间班组长岗位职责
2014/01/06 职场文书
单位创先争优活动方案
2014/01/26 职场文书
低碳环保倡议书
2014/04/14 职场文书
六查六看心得体会
2014/10/14 职场文书
新员工考核评语
2014/12/31 职场文书
确保工程质量承诺书
2015/04/29 职场文书
MySQL系列之十四 MySQL的高可用实现
2021/07/02 MySQL
OpenFeign实现远程调用
2022/08/14 Java/Android