前端埋点-小程序(uni-app)

257 阅读2分钟

前言

前段时间公司有要做埋点的需求,于是想封装一个插件化的埋点工具,接着总结小程序端中的常用埋点吧,因为公司用的是uni-app所以就以uni-app为主可能有部分会和原生不一样!🐶

生命周期

class wxLog {
    proxyApp (eventName,cb) {
        let that = this;
        const orgEvent = App;
        App = function (value) {
            let orgFun = value[eventName];
            value[eventName] = function () {
                cb.apply(that,[eventName,...arguments]);
                orgFun.apply(this,arguments)
            }
            orgEvent.apply(this,arguments)
        }   
    }
    AppControl (eventName,value) {
        let sendData = {
            kind:'behavior',
            type:'APPInit',
            eventName:eventName,
            time:new Date(),
        }
        // 发送逻辑忽略
    }
    init () {
        this.proxyApp('onLaunch',this.AppControl);
        this.proxyApp('onShow',this.AppControl);
        this.proxyApp('onHide',this.AppControl);
    }
}

路由

function getWxCurrentPage () {
    var pageList = getCurrentPages();
    var index = pageList.length -1;
    var current = pageList[index];
    if (current) {
        return current.route
    } else {
        return ''
    }
}
class wxLog {
    constructor (option) {
        this.isUniapp =option.isUniapp
    }
    setRoute (eventName,value) {
        if (value && value.url) {
            let currentPage = getWxCurrentPage();
            let toPath = value.url;
            let toRoute = getMiniPath(toPath);
            let now = new Date().getTime();
            let duration = now - this.currentTime;
            this.currentTime = now;
            let sendObj =  {
                kind:'behavior',
                type:'pageSwitch',
                currentPage,
                toRoute,
                eventName,
                duration,
            }
        }
    }
    setRouteBack (eventName,value) {
        if (value && value.delta) {
            if (Object.prototype.toString.call(value.delta) === '[object Number]') {
                let pageList = getCurrentPages();
                let toPage = pageList[pageList.length-1-value.delta];
                if (toPage && toPage.route) {
                    let currentPage = getWxCurrentPage();
                    let toPath = toPage.route;
                    let toRoute = getMiniPath(toPath);
                    let now = new Date().getTime();
                    let duration = now - this.currentTime;
                    this.currentTime = now;
                    let sendObj = {
                        kind:'behavior',
                        type:'pageSwitch',
                        currentPage,
                        toRoute,
                        eventName,
                        duration,
                    }
                }
            }
        }
    }
    proxy (event,eventName,cb) {
        let that = this;
        let orgEvent = event[eventName];
        event[eventName] = function () {
            cb.apply(that,[eventName,...arguments])
            orgEvent.apply(this,arguments)
        }
    }
    init () {
        if (this.isUniapp) {
            this.proxy(uni,'navigateTo',this.setRoute);
            this.proxy(uni,'redirectTo',this.setRoute);
            this.proxy(uni,'reLaunch',this.setRoute);
            this.proxy(uni,'navigateBack',this.setRouteBack);
        } else {
            this.proxy(wx,'navigateTo',this.setRoute);
            this.proxy(wx,'redirectTo',this.setRoute);
            this.proxy(wx,'reLaunch',this.setRoute);
            this.proxy(wx,'navigateBack',this.setRouteBack);
        }
    }
}

HTTP

class wxLog {
    requestControl (eventName,value) {
        let that = this;
        let sendData = {
            kind:'httpRequest',
            duration:'',
            status:'',
            requestUrl:'',
            method:'',
            body:'',
            response:'',
            time:getTime(),
            location:getWxCurrentPage(),
            responseHeader:'',
            errMsg:'',
        }
        let requestTime = new Date().getDate()
        let successFun = value.success;
        let failFun = value.fail;
        let sendType = value.sendType || ''
        sendData.body = value.data;
        sendData.method = value.method;
        sendData.requestHeader = value.header;
        sendData.requestUrl = value.url;
        value.success = function (response) {
            let responseTime = new Date().getDate()
            sendData.response = response.data;
            sendData.status = response.statusCode;
            sendData.responseHeader = response.header;
            sendData.errMsg = response.errMsg;
            sendData.duration = responseTime - requestTime;
            sendData.time = getTime();
            if (!sendType) // 发送埋点
            successFun.apply(this,arguments)
        }
        value.failFun = function (value) {
            sendData.response = value;
            sendData.status = 'fail';
            if (!sendType)  // 发送埋点
            failFun.apply(this,arguments)
        }
    }
    proxy (event,eventName,cb) {
        let that = this;
        let orgEvent = event[eventName];
        event[eventName] = function () {
            cb.apply(that,[eventName,...arguments])
            orgEvent.apply(this,arguments)
        }
    }
    init () {
        this.proxy(wx,'request',this.requestControl);
    }
}

JS报错

class wxLog {
    constructor (option,_vue) {
        this._vue = _vue;
    }
   proxyError () {
        this._vue.config.errorHandler = (err,vm,info) => {
            let sendData = {
                kind:'stability',
                type:err.name,
                errorMessage:err.message,
                time:getTime(),
                location:getWxCurrentPage(),
                errorStack:err.stack
            }
        };
    }
    init () {
        this.proxyError()
    }
}

点击事件

这里因为uni-app是通过一个__e的函数包装后调用的所以需要先判断是否是__e函数,在对其具体函数做重写

class wxLog {
    proxyEvent () {
        let that = this;
        const oldComponent = Component;
        Component = function (config) {
            Object.keys(config.methods).forEach(methodName => {
                if (methodName === "__e") {
                    that.proxyEventHandle(config.methods, methodName)
                }
            })
            oldComponent.apply(this,arguments)
        }
    }
    proxyEventHandle (methods,methodName) {
        let oldMethod = methods[methodName];
        let that = this;
        methods[methodName] = function () {
            let _vm = this.$vm;
            let eventObj = arguments[0];
            let type = eventObj['type'];
            if (that.eventList.indexOf(type)!=-1) { 
                let target = eventObj.currentTarget || {};
                let tapDomId = target.id?'#'+target.id:'';
                let dataset = target.dataset || {};
                let keys = Object.keys(that.eventController);
                let sendData = {
                    kind:'event',
                    type:type,
                    dom:tapDomId,
                    data:'',
                    location:getWxCurrentPage(),
                }
                if (!!dataset.eventOpts && type) {
                    if (type == "tap") { //只记录点击事件
                      const event_opts = dataset.eventOpts;
                      if (Array.isArray(event_opts) && event_opts[0].length === 2) {
                        let eventFunc = [];
                        event_opts[0][1].forEach(event => {
                          eventFunc.push({
                            name: event[0],
                            params: event[1] || ''
                          })
                        })
                        sendData.data = eventFunc;
                      }
                    }
                }
                
            oldMethod.apply(this,arguments)
        }
    }
    init () {
        this.proxyEvent()
    }
}