注入微信小程序实现数据采集

需求背景

以用户的维度来采集页面的UV,PV,等数据做统计

实现

思路就是对 hook 监听了,hook 每触发一次,就 to do something,目前微信小程序存在 hook 的,就是 APP,Page,Component。总不能在每个存在生命周期的地方去加一段 to do something 的代码,其一影响业务,其二维护和开发难度过高。

那么我们考虑以依赖的方式注入,可以尝试在 app.js 输出 App,Page和Component同理。

console.log(App)

相关 hook 文档

  1. App
  2. Page
  3. Component

从以上说明构造出以下的代码

// 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 = {

    }
})();

怎么注入?

call() 知识

/*举个例子*/

// 改写
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 即可实现效果,其实很可以做很多扩展,比如某个页面的函数执行触发之类的,如果哪里不对的,欢迎指出交流!

© 2024