起步
当业务上线后,用户经常反馈无法支付,或者点击区域之后,无法获得对应的数据。
抑或是,为了分析出那些区域、模块点击的多,以及分析用户画像诸如此类的业务,才有此业务。
手动埋点
在页面中手动埋点不仅费时,更加费力,甚至会污染业务代码。
最常见的埋点的代码是这样子的:
<template>
<view>
<button @click.stop="tracker">x</button>
</view>
</tempale>
<script>
export default {
methonds:{
tracker(e){
// Do some work code.
fetch("/report",e);
}
}
}
</script>
这样的代码简单易懂,但是将其难以管理。如果后期出现了需要埋点的页面、或者移除埋点地方,那么就会导致漫长的时间,需要修改代码。
自动埋点
修改 App
在uni-app
中,App.vue
文件可以作为应用启动文件。它包含了onLaunch
、onShow
、onHide
、onError
四种生命周期。
分别对应的是:
- 小程序初始化
- 小程序启动或切到前台
- 小程序切后台
还有一个选项就是globalData
,它是全局数据,它能让所有的页面都可以访问到其数据。
App.vue
内的代码是典型的 Vue
风格的代码,如下:
<script>
export default {
onLaunch: function() {
console.log('App Launch');
},
onShow: function() {
console.log('App Show');
},
onHide: function() {
console.log('App Hide');
}
};
</script>
<style></style>
不过我们可以将它改造一下,让它变成可委托的形式:
const __entrustApp = App;
App = app => {
['onLounch', 'onShow', 'onHide', 'onError'].forEach(lifeCycle => {
app[lifeCycle] = () => {
console.log('--->lifeCycle', lifeCycle);
};
});
__entrustApp(app);
};
这样,就可以看到各个生命周期的日志了。
修改 Page
同理,Page
也是可以改写为委托的形式,和App
相似,依旧可以委托。
const __entrustPage = Page;
Page = page => {
['onLoad', 'onShow', 'onReady', 'onHide'].forEach(lifeCycle => {
page[lifeCycle] = () => {
// Do something...
};
});
return __entrustPage(page);
};
但是,uni-app
高度封装了Page
代码,也做了一系列的埋点:
function initHook(name, options) {
const oldHook = options[name];
if (!oldHook) {
options[name] = function() {
initTriggerEvent(this);
};
} else {
options[name] = function(...args) {
initTriggerEvent(this);
return oldHook.apply(this, args);
};
}
}
Page = function(options = {}) {
initHook('onLoad', options);
return MPPage(options);
};
但是为了获取页面内的埋点信息,考虑下面的代码:
<template>
<view>
<button @click.stop="entrustEvent">Clike me</button>
</view>
</template>
<script>
export default {
methonds: {
entrustEvent(e) {}
}
};
</script>
使用Mixin
其中entrustEvent
中的e
参数代表了该元素传递过来的信息。
既然如此,那么可以使用mixin
重新改写一下此函数。
export default {
methonds: {
initTracker() {
const __entrstNativeEvent = this['entrustEvent'];
this['entrustEvent'] = e => {
console.log('Event before launch');
__entrstNativeEvent(e);
console.log('Event after launch');
};
}
}
};
Event before
和Event after
分别原生的代码的之前和之后运行的日志。
此时,我们需要将需要埋点的函数和页面写入到埋点器中,并且作为一个全局mixin
函数内嵌到Vue
中:
class Tracker {
constructor(trackerOpt) {
this.fns = trackerOpt.fns;
Vue.mixin(this.initMixin);
}
initMixin() {
const { fns } = this;
return {
onLoad() {
this.hijack();
},
methonds: {
hijack() {
let that = this;
fns.forEach(element => {
const __entrustFn = that[element];
that[element] = event => {
console.log('Before run.');
__entrustFn(event);
console.log('After run.');
};
});
}
}
};
}
}
其中这一块代码和上面原理的相似,只不过传入了函数fns
参数,让它自动拦截函数。
每次触发函数时,就可以返回函数的埋点信息了。
event
参数如下:
{
"changedTouches": [{}],
"currentTarget": { "id": "", "offsetLeft": 0, "offsetTop": 0, "dataset": {} },
"detail": { "x": 262, "y": 68 },
"mark": {},
"mp": {
"type": "tap",
"timeStamp": 9779,
"target": {},
"currentTarget": {},
"mark": {}
},
"mut": false,
"preventDefault": {},
"stopPropagation": {},
"target": {
"id": "",
"offsetLeft": 0,
"offsetTop": 0,
"dataset": {},
"x": 262
},
"timeStamp": 9779,
"touches": [{}],
"type": "tap"
}
其中,detail
参数是该函数,以及关于其他数据。再加上一个函数接受它的参数,以便于上报。
只需要加上如下代码就可以了:
class Tracker {
constructor(trackerOpt) {
this.fns = trackerOpt.fns;
this.reportFn = trackerOpt.reportFn;
Vue.mixin(this.initMixin);
}
initMixin() {
+ const { fns, reportFn } = this;
return {
onLoad() {
this.hijack();
},
methonds: {
hijack() {
let that = this;
fns.forEach(element => {
const __entrustFn = that[element];
that[element] = event => {
console.log('Before run.');
__entrustFn(event);
+ reportFn.call(null,event);
console.log('After run.');
};
});
}
}
};
}
}
这样,外层的reportFn
就可以直接接收到埋点的数据。