JS实现监控微信小程序的原理


Posted in Javascript onJune 15, 2018

原理

之前也做过浏览器web端的SDK数据埋点上报,其实原理大同小异:通过劫持原始方法,获取需要上报的数据,最后再执行原始方法,这样就能实现无痕埋点。

举个例子:我希望监控所有web页面的ajax请求,每次发送ajax,都需要在控制台打印出发送的url

平时我们开发,发送ajax一般用的都是封装好的库,例如jQuery,Axios等,然而这些库,底层仍然用的是浏览器原生的XMLHttpRequest对象,因此,我们只需要修改XMLHttpRequest对象即可

注意:由于JS的灵活性,修改原生方法是一件很容易的事,然而并不鼓励这样做!

// 把这段代码放在所有JS代码之前,我们就实现了拦截ajax的需求
window.XMLHttpRequest.prototype.open = (function(originOpen) {
 return function(method, url, async) {
  
  console.log('发送了ajax,url是: ', url);

  return originOpen.apply(this, arguments);
 };
})(window.XMLHttpRequest.prototype.open);

在这个立即执行函数中,我们把原生的 open 方法通过 originOpen 暂时存储起来,然后在外面包裹一层函数,实现了打印输出url的功能,最后通过 originOpen.apply 让原生方法运行,这样就实现了无痕拦截。

监控小程序

拦截wx.request

小程序的运行环境并没有 window 和 document 对象,它只暴露了一个 wx 全局对象,发送网络请求则是通过wx.request这个api,因此,这次我们需要拦截的就是 wx.request 方法

我们试着更改一下 wx.request

wx.request = function() {
 console.log('66666');
}

这时控制台会报错 TypeError: Cannot set property request of #<Object> which has only a getter

这是因为, wx.request 这个属性,只有 get 方法而没有 set 方法,我们可以通过 Object.getOwnPropertyDescriptor 验证:

const des = Object.getOwnPropertyDescriptor(wx, 'request');

// des {
// configurable: true,
// enumerable: true,
// get: f(),
// set: undefined
// }

我们可以换种方式修改:

const originRequest = wx.request;
Object.defineProperty(wx, 'request', {
 configurable: true,
 enumerable: true,
 writable: true,
 value: function() {
  const config = arguments[0] || {};
  const url = config.url;
  console.log('发送了ajax,url是: ', url);

  return originRequest.apply(this, arguments);
 }
});

这次就实现拦截功能了!

监控异常

小程序的注册函数 App 有个全局的 onError 方法,我们可以在小程序的入口文件 app.js 先注册一个该方法:

App({ onError: function(err) {
  console.log('上报错误啦!');
  wx.request({
   url: 'http://monitor.com/monitor/error',
   data: err
  })
 }
})
App({
 // 其他逻辑
})

不过需要注意的是:如果后续的程序重写了onError的话,将会导致之前注册的onError失效。

解决方法可以是:我们监控SDK可以暴露一个接口,让接入方自己在onError中调用我们的接口。

App({
 onError: function (err) {
 monitor.notifyError(err)
 }
})

上报数据

收集好需要的数据后,当然就要上报后台。怎么上报?当然还是用的 wx.request 发送请求。

这里就容易出现一个 死循环 : 如果用之前被我们包装过的 wx.request 上报数据,那么上报数据这个ajax请求,也会被我们认为是普通的ajax请求,然后又会触发上报,这样来来回回,无穷无尽的发送上报数据。

解决方法有多种,比如:

方案1

可以在包装 wx.request 的时候,判断发送的url如果是上报接口,那么就不再上报了。

const originRequest = wx.request;
Object.defineProperty(wx, 'request', {
 configurable: true,
 enumerable: true,
 writable: true,
 value: function() {
  const config = arguments[0] || {};
  const url = config.url;
  if (url.indexOf('http://monitor.com') > -1) {
   // 直接发送请求,不上报
   return originRequest.apply(this, arguments);
  }
  console.log('上报ajax数据啦!');
  wx.request({
   url: 'http://monitor.com/monitor/ajax',
   data: config.data
  })
  return originRequest.apply(this, arguments);
 }
});

方案2

在包装 wx.request 之前,保留一份最原始的 wx.request 方法,所有的上报请求,就不走被包装过的方法,而走最原始的方法。

const myRequest = wx.request;
const wrapRequest = function () {
 const originRequest = wx.request;
 Object.defineProperty(wx, 'request', {
  configurable: true,
  enumerable: true,
  writable: true,
  value: function() {
   const config = arguments[0] || {};
   const url = config.url;
   console.log('上报数据啦!');
   // 使用最原始的request方法
   myRequest({
    url: 'http://monitor.com/monitor/ajax',
    data: config.data
   })
   return originRequest.apply(this, arguments);
  }
 });
}
wrapRequest();

其他事项

实际开发中当然还有更多的细节,比如监控项目的鉴权,SDK的代码结构,上报前的数据收集和聚合等等,本文就不详细展开了。

总结

以上所述是小编给大家介绍的JS实现监控微信小程序的原理,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
使用jQuery的将桌面应用程序引入浏览器
Nov 19 Javascript
初窥JQuery(二) 事件机制(1)
Nov 25 Javascript
Draggable Elements 元素拖拽功能实现代码
Mar 30 Javascript
再谈Jquery Ajax方法传递到action(补充)
May 12 Javascript
javascript实现节点(div)名称编辑
Dec 17 Javascript
js使用onmousemove和onmouseout获取鼠标坐标的方法
Mar 31 Javascript
jquery根据td给相同tr下其他td赋值的实现方法
Oct 05 Javascript
在JS中a标签加入单击事件屏蔽href跳转页面
Dec 16 Javascript
JQuery页面随滚动条动态加载效果的简单实现(推荐)
Feb 08 Javascript
Koa 使用小技巧(小结)
Oct 22 Javascript
jQuery单页面文字搜索插件jquery.fullsearch.js的使用方法
Feb 04 jQuery
vuex 多模块时 模块内部的mutation和action的调用方式
Jul 24 Javascript
vue .sync修饰符的使用详解
Jun 15 #Javascript
vue项目webpack中Npm传递参数配置不同域名接口
Jun 15 #Javascript
vue和webpack项目构建过程常用的npm命令详解
Jun 15 #Javascript
vue和webpack安装命令详解
Jun 15 #Javascript
Webpack devServer中的 proxy 实现跨域的解决
Jun 15 #Javascript
详解webpack的proxyTable无效的解决方案
Jun 15 #Javascript
vue中axios的封装问题(简易版拦截,get,post)
Jun 15 #Javascript
You might like
php的大小写敏感问题整理
2011/12/29 PHP
php 中的4种标记风格介绍
2012/05/10 PHP
PHP中的empty、isset、isnull的区别与使用实例
2019/03/22 PHP
php数组函数array_push()、array_pop()及array_shift()简单用法示例
2020/01/26 PHP
jQuery toggle()设置CSS样式
2009/11/05 Javascript
AJAX的跨域与JSONP(为文章自动添加短址的功能)
2010/01/17 Javascript
关于Mozilla浏览器不支持innerText的解决办法
2011/01/01 Javascript
ExtJS中文乱码之GBK格式编码解决方案及代码
2013/01/20 Javascript
JS将制定内容复制到剪切板示例代码
2014/02/11 Javascript
jQuery遍历Table应用示例
2014/04/09 Javascript
JavaScript获取文本框内选中文本的方法
2015/02/20 Javascript
js实现动画特效的文字链接鼠标悬停提示的方法
2015/03/02 Javascript
jquery Easyui快速开发总结
2015/08/20 Javascript
在javascript中随机数 math random如何生成指定范围数值的随机数
2015/10/21 Javascript
jQuery+css实现的tab切换标签(兼容各浏览器)
2016/01/28 Javascript
JavaScript实现相册弹窗功能(zepto.js)
2016/06/21 Javascript
Node连接mysql数据库方法介绍
2017/02/07 Javascript
基于jquery实现多选下拉列表
2017/08/02 jQuery
详解如何优雅地在React项目中使用Redux
2017/12/28 Javascript
js+canvas实现验证码功能
2020/09/21 Javascript
vue使用微信JS-SDK实现分享功能
2019/08/23 Javascript
JS实现按比例缩小图片宽高
2020/08/24 Javascript
[08:42]DOTA2每周TOP10 精彩击杀集锦vol.2
2014/06/25 DOTA
python encode和decode的妙用
2009/09/02 Python
Python的Flask框架中Flask-Admin库的简单入门指引
2015/04/07 Python
Python实现简单的多任务mysql转xml的方法
2017/02/08 Python
pytorch 在网络中添加可训练参数,修改预训练权重文件的方法
2019/08/17 Python
俄罗斯珠宝市场的领导者之一:Бронницкий ювелир
2019/10/02 全球购物
如何用Java判断一个文件或目录是否存在
2012/11/19 面试题
中国梦的演讲稿
2014/01/08 职场文书
小学语文教学经验交流材料
2014/06/02 职场文书
2016年三八节红领巾广播稿
2015/12/17 职场文书
2016年小学“感恩教师”主题队日活动总结
2016/04/01 职场文书
Nginx配置SSL证书出错解决方案
2021/03/31 Servers
使用Redis实现秒杀功能的简单方法
2021/05/08 Redis
Pillow图像处理库安装及使用
2022/04/12 Python