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


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 相关文章推荐
jquery实现标签支持图文排列带上下箭头按钮的选项卡
Mar 14 Javascript
kindeditor修复会替换script内容的问题
Apr 03 Javascript
jQuery EasyUI基础教程之EasyUI常用组件(推荐)
Jul 15 Javascript
简单的js计算器实现
Oct 26 Javascript
详解基于webpack2.x的vue2.x的多页面站点
Aug 21 Javascript
WebPack配置vue多页面的技巧
May 15 Javascript
vue中导出Excel表格的实现代码
Oct 18 Javascript
详解react native页面间传递数据的几种方式
Nov 07 Javascript
typescript配置alias的详细步骤
Aug 12 Javascript
vscode中的vue项目报错Property ‘xxx‘ does not exist on type ‘CombinedVueInstance<{ readyOnly...Vetur(2339)
Sep 11 Javascript
原生JS实现京东查看商品点击放大
Dec 21 Javascript
SSM VUE Axios详解
Oct 05 Vue.js
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处理excel cvs表格的方法实例介绍
2013/05/13 PHP
php绘图中显示不出图片的原因及解决
2014/03/05 PHP
php中访问修饰符的知识点总结
2019/01/27 PHP
javascript 字符 Escape,encodeURI,encodeURIComponent
2009/07/09 Javascript
javascript 添加和移除函数的通用方法
2009/10/20 Javascript
不一样的文字闪烁 轮番闪烁
2009/11/11 Javascript
javascript在事件监听方面的兼容性小结
2010/04/07 Javascript
js Html结构转字符串形式显示代码
2011/11/15 Javascript
网页中可关闭的漂浮窗口实现可自行调节
2013/08/20 Javascript
基于javascript实现的搜索时自动提示功能
2014/12/26 Javascript
Javascript中的方法链(Method Chaining)介绍
2015/03/15 Javascript
JS+CSS实现简单滑动门(滑动菜单)效果
2015/09/19 Javascript
整理JavaScript创建对象的八种方法
2015/11/03 Javascript
学习JavaScript设计模式(单例模式)
2015/11/26 Javascript
jQuery中JSONP的两种实现方式详解
2016/09/26 Javascript
微信端开发--登录小程序步骤
2017/01/11 Javascript
jQuery实现打开网页自动弹出遮罩层或点击弹出遮罩层功能示例
2017/10/19 jQuery
Vue底层实现原理总结
2018/02/17 Javascript
vue cli使用融云实现聊天功能的实例代码
2019/04/19 Javascript
JS JQuery获取data-*属性值方法解析
2020/09/01 jQuery
用Python编写一个简单的Lisp解释器的教程
2015/04/03 Python
Python 包含汉字的文件读写之每行末尾加上特定字符
2016/12/12 Python
python机器学习之神经网络(二)
2017/12/20 Python
vue学习笔记之动态组件和v-once指令简单示例
2020/02/29 Python
Python astype(np.float)函数使用方法解析
2020/06/08 Python
The North Face北面美国官网:美国著名户外品牌
2018/09/15 全球购物
伦敦最受欢迎的蛋糕店:Konditor & Cook
2019/11/01 全球购物
网上常见的一份Linux面试题(多项选择部分)
2014/09/09 面试题
高校生生产实习自我鉴定
2013/09/21 职场文书
总裁助理岗位职责
2014/02/17 职场文书
入党综合考察材料
2014/06/02 职场文书
数据保密承诺书
2014/06/03 职场文书
2015年大学生实习评语
2015/03/25 职场文书
2015中学教师个人工作总结
2015/07/22 职场文书
社会实践心得体会范文
2016/01/14 职场文书
Python+腾讯云服务器实现每日自动健康打卡
2021/12/06 Python