uni-app 埋点方案
以实现方式划分可以分为 "无埋点” 和 "手动埋点 "。
无埋点
无埋点介绍两种实现方式,分别使用 uni-app 拦截器和 vue 的 mixins。
使用无埋点方案统计,需要我们改造一下 pages.json
文件,uni-app 会自动加载 src
目录下的 pages.js
文件。
pages.json
文件不能删除,内容可以为空。
// src/pages.json
{
"tip": "please use pages.js"
}
json
// src/pages.js
module.exports = () => ({
pages: [
{
path: "pages/StartPage/StartPage",
style: {
navigationBarTitleText: "登录",
navigationBarBackgroundColor: "#fff",
navigationBarTextStyle: "white",
},
},
{
path: "pages/Dashboard/Dashboard",
stat: true,
module: "模块名称1",
style: {
navigationBarBackgroundColor: "#fff",
navigationBarTextStyle: "white"
},
},
],
subPackages: [
{
root: "packageTest",
pages: [
{
path: "pages/Test/Test",
stat: true,
module: '模块名称2',
style: {
navigationBarBackgroundColor: "#fff",
navigationBarTextStyle: "white",
navigationBarTitleText: "模块名称2",
},
},
],
}
],
});
js
在 pages.js
文件中,我们使用 stat
字段标识模块是否需要统计,使用 module
字段作为模块名称。
下面是使用的工具方法。
// util.js
import pages from '../../pages';
function curry(func) {
return function curriedFn (...args) {
if (args.length < func.length) {
return function () {
return curriedFn(...args.concat(Array.from(arguments)));
}
}
return func(...args);
}
}
const joinPath = (root, item) => (item.path = `${root}/${item.path}`, item);
const arrToObj = (pages) =>
pages.reduce((pre, cur) => (pre = { ...pre, [cur.path]: cur.module }, pre), {});
const statFilterStrategy = (item) => item.stat;
// 埋点页面统计
const getStatPages = ({ pages, subPackages }) =>
subPackages.reduce(
(pre, cur) =>
pre.concat(cur?.pages.filter(statFilterStrategy).map(item => joinPath(cur.root, item)) || []),
pages?.filter(statFilterStrategy) || []
);
// 埋点页面对象
// { [path]: [module] }
const getStatObj = () => {
return arrToObj(getStatPages(pages()))
}
module.exports = {
curry,
joinPath,
arrToObj,
getStatPages,
getStatObj
}
js
uni-app 拦截器
我们可以使用 uni-app 提供的拦截器,拦截全局路由跳转。
缺点:uni-app 拦截器,不会统计页面回退,也就是说你只能统计进入当前页面,如果进入其他页面回退是无法统计到的。
代码实现如下:
// useStatIntercept.js
import { curry, getStatObj } from './utils';
// 无埋点处理
export const useStatIntercept = () => {
const apis = ['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'];
const statObj = getStatObj;
apis.forEach(api => {
uni.addInterceptor(api, {
invoke(e) {
const url = (e.url.split('?')[0]).replace(/^\//, '');
if (statObj[url]) {
statUpload(url, statObj[url]);
}
lastStat = {
path: url,
module: statObj[url]
}
}
})
});
}
// 手动埋点处理
export const statUpload = curry((path, module) => {
const app = getApp();
const { branch_id } = app?.globalData || {};
if (!module || !branch_id) return;
console.log('[stat upload]', module, path);
// TODO:自定义实现上报逻辑
})
js
我们可以在 App.vue
文件中使用它。
<script>
import { useStatIntercept } from '@/utils/stat/useStatIntercept';
export default {
onLaunch () {
useStatIntercept(); // 用户使用记录统计
}
}
</script>
全局 mixins
由于通过 uni-app 拦截器拦截路由,页面回退时无法统计到。所以我们还可以使用全局注册 mixins 的方式。
import { statUpload } from './useStatIntercept';
import { getStatObj } from './utils';
export default {
onShow() {
const { route: path } = getCurrentPages().slice(-1)[0] || {};
const module = getStatObj()[path];
statUpload(path, module);
}
}
js
使用方式很简单,从 main.js
文件中导入注册即可。
// main.js
import StatMixin from '@/utils/stat/useStatMixin';
Vue.mixin(StatMixin);
js
手动埋点
手动埋点可以用来解决一些多样化的需求。假设当前页面并不只是统计进入和退出,而是需要根据页面内多个子模块的点击进行埋点。
由于我们的 statUpload
方法经过柯里化函数进行处理,所以我们可以这样使用。
<script>
import { statUpload } from '@/utils/stat/useStatIntercept';
// statUpload 经过柯里化处理,可以提供多次传参(默认传递当前页面路径)
const statLogger = statUpload('packageTest/pages/Test/Test');
export default {
methods: {
switch () {
// 切换模块时,可以传递不同的名称,埋点上报
statLogger('xxxx01');
statLogger('xxxx02');
}
}
}
</script>
总结
本篇文章介绍了埋点的几种实现方案,在真实业务场景中需要我们根据实际需求去实现自己的埋点 sdk
。