注入微信小程序实现数据采集
需求背景
以用户的维度来采集页面的UV,PV,等数据做统计
实现
思路就是对 hook 监听了,hook 每触发一次,就 to do something,目前微信小程序存在 hook 的,就是 APP,Page,Component。总不能在每个存在生命周期的地方去加一段 to do something 的代码,其一影响业务,其二维护和开发难度过高。
那么我们考虑以依赖的方式注入,可以尝试在 app.js 输出 App,Page和Component同理。
console.log(App)
相关 hook 文档
从以上说明构造出以下的代码
// collect.js
(() => {
let _App = App;
let _Page = Page;
let _Component = Component;
// 需监听的生命周期
let appDefaultHookList = ['onLaunch', 'onShow', 'onHide', 'onError', 'onPageNotFound'];
let pageDefaultHookList = ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage'];
const collect = {
}
})();
怎么注入?
/*举个例子*/
// 改写
console.log(1); // 1
window.console.log = text => `${text}, add...`;
console.log(1); // "1, add..."
// 改写后追加
let _Number = Number;
Number = function(args){ console.log(args); return _Number.call(this, args); }
Number('1');
//这个时候会输出"1, add..."和返回Int 1
从以上原理得出以下代码
// collect.js
(() => {
let _App = App;
let _Page = Page;
let _Component = Component;
// 需监听的生命周期
let appDefaultHookList = ['onLaunch', 'onShow', 'onHide', 'onError', 'onPageNotFound'];
let pageDefaultHookList = ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage'];
const collect = {
hookTriggerHandle(instance, hook, scene){
// 有些hook可能没有被注册所以得判断一下是否存在
if(instance.hasOwnProperty(hook)){
let pristine = instance[hook];
instance[hook] = function(args){
collect.sendRubbish.call(this, args, hook, scene);
return pristine.call(this, args);
}
}else if(scene == 'PAGE'){
if(hook == pageDefaultHookList[1] || hook == pageDefaultHookList[3] || hook == pageDefaultHookList[4]){
instance[hook] = function(args){
collect.sendRubbish.call(this, args, hook, scene);
}
}
}
}
},
}
// injection
App = (me) => {
appDefaultHookList.map(hookName => collect.hookTriggerHandle(me, hookName, 'APP'));
_App(me);
}
Page = (me) => {
pageDefaultHookList.map(hookName => collect.hookTriggerHandle(me, hookName, 'PAGE'));
_Page(me);
}
Component = (me) => {
//do something...
_Component(me);
}
})();
发送数据给服务端
// collect.js
(() => {
let _App = App;
let _Page = Page;
let _Component = Component;
// 需监听的生命周期
let appDefaultHookList = ['onLaunch', 'onShow', 'onHide', 'onError', 'onPageNotFound'];
let pageDefaultHookList = ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage'];
const collect = {
hookTriggerHandle(instance, hook, scene){
// 有些hook可能没有被注册所以得判断一下是否存在
if(instance.hasOwnProperty(hook)){
let pristine = instance[hook];
instance[hook] = function(args){
collect.sendRubbish.call(this, args, hook, scene);
return pristine.call(this, args);
}
}else if(scene == 'PAGE'){
if(hook == pageDefaultHookList[1] || hook == pageDefaultHookList[3] || hook == pageDefaultHookList[4]){
instance[hook] = function(args){
collect.sendRubbish.call(this, args, hook, scene);
}
}
}
}
},
sendRubbish(data, hook, scene){
if(scene == 'APP'){
// onLaunch
if(hook == appDefaultHookList[0]){
// 场景值
collect.data.scene = data.scene;
collect.collectGarbage.call(this, {
args: null,
event: hook,
stayTime: null,
createUnix: +new Date(),
other: data.query,
isFirstOpen: true
});
}
}else if(scene == 'PAGE'){
// special handle
if(hook == pageDefaultHookList[1]){
// onShow
if(this.__route__ != collect.data.prevPageInfo.url){
// mark
collect.data.prevPageInfo = {
args: data || null,
event: hook,
stayTime: null,
createUnix: +new Date(),
other: this.options || null
}
}
}
}
/*
push 发送给服务端
collectGarbage 函数就不细写出来了,就是wx.request
*/
collect.collectGarbage.call(this, {
args: data || null,
event: hook,
stayTime: 0,
createUnix: +new Date(),
other: this.options || null
});
}
}
// injection
App = (me) => {
appDefaultHookList.map(hookName => collect.hookTriggerHandle(me, hookName, 'APP'));
_App(me);
}
Page = (me) => {
pageDefaultHookList.map(hookName => collect.hookTriggerHandle(me, hookName, 'PAGE'));
_Page(me);
}
Component = (me) => {
//do something...
_Component(me);
}
})();
总结
最后在 app.js 引入 collect.js 即可实现效果,其实很可以做很多扩展,比如某个页面的函数执行触发之类的,如果哪里不对的,欢迎指出交流!