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


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 typeof 用法
Dec 28 Javascript
jquery 分页控件实现代码
Nov 30 Javascript
IE6 fixed的完美解决方案
Mar 31 Javascript
jQuery+ajax实现动态执行脚本的方法
Jan 27 Javascript
javascript实现点击按钮弹出一个可关闭层窗口同时网页背景变灰的方法
May 13 Javascript
js基于myFocus实现轮播图效果
Feb 14 Javascript
js 博客内容进度插件详解
Feb 19 Javascript
vue2实现搜索结果中的搜索关键字高亮的代码
Aug 29 Javascript
Bootstrap Table 双击、单击行获取该行及全表内容
Aug 31 Javascript
在vue项目中引入highcharts图表的方法
Jan 21 Javascript
浅谈JavaScript闭包
Apr 09 Javascript
jQuery实现图片切换效果
Oct 19 jQuery
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作为Shell脚本语言使用
2006/10/09 PHP
构建简单的Webmail系统
2006/10/09 PHP
php5数字型字符串加解密代码
2008/04/24 PHP
php加水印的代码(支持半透明透明打水印,支持png透明背景)
2013/01/17 PHP
PHP实现根据浏览器跳转不同语言页面代码
2013/08/02 PHP
Linux下创建nginx脚本-start、stop、reload…
2014/08/03 PHP
你不知道的文件上传漏洞php代码分析
2016/09/29 PHP
详谈php ip2long 出现负数的原因及解决方法
2017/04/05 PHP
Laravel 5.4向IoC容器中添加自定义类的方法示例
2017/08/15 PHP
Jquery实战_读书笔记2 选择器
2010/01/22 Javascript
JS表格组件BootstrapTable行内编辑解决方案x-editable
2016/09/01 Javascript
jquery.multiselect多选下拉框实现代码
2016/11/11 Javascript
jquery中绑定事件的异同
2017/02/28 Javascript
jQuery读取本地的json文件(实例讲解)
2017/10/31 jQuery
在vue中配置不同的代理同时访问不同的后台操作
2020/09/11 Javascript
[01:36:19]Secret vs NB 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
[55:18]Liquid vs Chaos 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
Python创建xml的方法
2015/03/10 Python
Python实现分割文件及合并文件的方法
2015/07/10 Python
Python数据可视化教程之Matplotlib实现各种图表实例
2019/01/13 Python
使用Python自动生成HTML的方法示例
2019/08/06 Python
python多进程重复加载的解决方式
2019/12/13 Python
tensorflow 重置/清除计算图的实现
2020/01/19 Python
详解Python流程控制语句
2020/10/28 Python
html5仿支付宝密码框的实现代码
2017/09/06 HTML / CSS
狗狗玩具、零食和咀嚼物的月度送货服务:Super Chewer
2018/08/22 全球购物
英国No.1体育用品零售商:SportsDirect.com
2019/10/16 全球购物
乌鸦喝水教学反思
2014/02/07 职场文书
自我鉴定书
2014/03/24 职场文书
个人投资计划书
2014/05/01 职场文书
思想纪律作风整顿剖析材料
2014/10/11 职场文书
清洁工岗位职责
2015/02/13 职场文书
广告公司文案策划岗位职责
2015/04/14 职场文书
2016年端午节红领巾广播稿
2015/12/18 职场文书
学术会议开幕词
2016/03/03 职场文书
python playwrigh框架入门安装使用
2022/07/23 Python