社区精选 | 搭建前端监控,采集用户行为的 N 种姿势
今天为各位带来的是社区作者 杨成功 的文章,在这篇文章中他为大家介绍了搭建前端监控,采集用户行为的 N 种姿势。
大家好,我是杨成功。
上一篇我们详细介绍了前端如何采集异常数据。采集异常数据是为了随时监测线上项目的运行情况,发现问题及时修复。在很多场景下,除了异常监控有用,收集用户的行为数据同样有意义。
怎么定义行为数据?顾名思义,就是用户在使用产品过程中产生的行为轨迹。比如去过哪几个页面,点过哪几个按钮,甚至在某个页面停留了多长时间,某个按钮点击了多少次,如果有需求都可以记录下来。
但是记录行为数据是一个和业务紧密关联的事情,不可能把每个用户每一步操作都极其详细的记录下来,这样会产生极其庞大的数据,很显然不现实。
合理的做法是,根据产品的实际情况评估,哪个模块哪个按钮需要重点记录,则可以采集的详细一些;哪些模块不需要重点关注,则简单记录一下基本信息。
根据这个逻辑,我们可以把行为数据分为两类:
通用数据 特定数据
通用数据
// Vue3 路由写法
const router = createRouter({ ... })
router.beforeEach(to => {
// to 代表新页面的路由对象
recordBehaviors(to)
})
import HomePage from '@/pages/Home'
<Route path="*" component={HomePage} />,
然后在这个组件的的 useEffect 中监听路由变化:
// HomePage.jsx
const { pathname } = useLocation();
useEffect(() => {
// 路由切换这个函数触发
recordBehaviors(pathname);
}, [pathname]);
app:应用的名称/标识 env:应用环境,一般是开发,测试,生产 version:应用的版本号 user_id:当前用户 ID user_name:当前用户名 page_route:页面路由 page_title:页面名称 start_at:进入时间 end_at:离开时间
获取用户信息
// 从状态管理里中导出用户数据
import { UserStore } from '@/stores';
let { user_id, user_name } = UserStore;
这里的 @/stores 指向我项目中的文件 src/stores/index.ts,表示状态管理的入口文件,使用时替换成自己项目的实际位置。实际情况中还会有用户数据为空的问题,这里需要单独处理一下,方便我们在后续的数据查看中能看出分别:
import { UserStore } from '@/stores';
// 收集行为函数
const recordBehaviors = ()=> {
let report_date = {
...
}
if(UserStore) {
let { user_id, user_name} = UserStore
report_date.user_id = user_id || 0
report_date.user_name = user_name || '未命名'
} else {
report_date.user_id = user_id || -1
report_date.user_name = user_name || '未获取'
}
}
user_id 为 0:表示有用户数据,但没有 user_id 字段或该字段为空 user_id 为 -1:表示没有用户数据,因而 user_id 字段获取不到
获取页面信息
// 路由配置
{
path: '/test',
meta: {
title: '测试页面'
},
component: () => import('@/views/test/Index.vue')
}
// 获取配置
const recordBehaviors = (to)=> {
let page_route = to.path
let page_title = to.meta.title
}
// React 中
import { RouteStore } from '@/stores';
const recordBehaviors = (pathname) => {
let { routers } = RouteStore; // 取出路由数据
let route = routers.find((row) => (row.path = pathname));
if (route) {
let page_route = route.path;
let page_title = route.title;
}
};
设置时间
某个用户在某个页面停留了多久? 某个段时间内,某个用户停留在哪几个页面? 某个时间段内,哪个页面的用户停留时间最长? 某个页面,哪些用户的使用率最高?
var start_at = new Date();
// 进入页面时调用
const recordBehaviors = () => {
let report_date = {...} // 此时 end_at 为空
http.post('/behaviors/insert', report_date).then(res=> {
let id = res.id // 数据 id
localStorage.setItem('CURRENT_BEHAVIOR_ID', id)
})
}
// 离开页面时调用:
const updateBehaviors = ()=> {
let id = localStorage.getItem('CURRENT_BEHAVIOR_ID')
let end_at = new Date()
http.post('/behaviors/update/'+id, end_at) // 根据 id 更新结束时间
localStorage.removeItem('CURRENT_BEHAVIOR_ID')
}
// 进入页面时调用
const recordBehaviors = () => {
let report_date = {...} // 此时 end_at 为空
localStorage.setItem('CURRENT_BEHAVIOR', JSON.stringify(report_date));
}
// 离开页面时调用
const reportBehaviors = () => {
let end_at = new Date()
let report_str = localStorage.getItem('CURRENT_BEHAVIOR')
if(report_str) {
let report_date = JSON.parse(report_str)
report_date.end_at = end_at
http.post('/behaviors/insert', report_date)
} else {
console.log('无行为数据')
}
}
特定数据
<button onClick={onClick}>点击</button>;
const onClick = (e) => {
// console.log(e);
repoerEvents(e);
};
app:应用的名称/标识 env:应用环境,一般是开发,测试,生产 version:应用的版本号 user_id:当前用户 ID user_name:当前用户名 page_route:页面路由 page_title:页面名称 created_at:触发时间 event_type:事件类型 action_tag:行为标识 action_label:行为描述
event_type:事件类型 action_tag:行为标识 action_label:行为描述
手动埋点上报
<button data-tag="user_login" data-label="用户登录" onClick={onClick}>
登录
</button>;
const onClick = (e) => {
// console.log(e);
repoerEvents(e);
};
// 埋点上报函数
const repoerEvents = (e)=> {
let report_date = {...}
let { tag, label } = e.target.dataset
if(!tag || !label) {
return new Error('上报元素属性缺失')
}
report_date.event_type = e.type
report_date.action_tag = tag
report_date.action_label = label
// 上报数据
http.post('/events/insert', report_date)
}
全局自动上报
必须为元素指定事件处理函数 必须为元素添加自定义属性 在原有事件处理函数中手动添加埋点,侵入性高
window.addEventListener('click', (event) => {
let { tag, label, trigger } = event.target.dataset;
if (tag && label && trigger == 'click') {
// 说明该元素需要埋点
repoerEvents(event);
}
});
<button data-tag="form_save" data-label="表单保存" data-trigger="click">
保存
</button>
组件上报
import { useEffect, useRef } from 'react';
const CusReport = (props) => {
const dom = useRef(null);
const handelEvent = () => {
console.log(props); // {tag:xx, label:xx, trigger:xx}
repoerEvents(props);
};
useEffect(() => {
if (dom.current instanceof HTMLElement) {
dom.current.addEventListener(props.trigger, handelEvent);
}
}, []);
return (
<span ref={dom} className="custom-report">
{props.children}
</span>
);
};
export default CusReport;
<CusReport tag="test" label="功能测试" trigger="click">
<button>测试</button>
</CusReport>
这样就比较优雅了,不需要修改目标元素,只要把组件包裹在目标元素之外即可。
总结
SegmentFault 思否社区小编说
自 2022-07-01 起 SegmentFault 思否公众号改版啦!之后将陆续推出新的栏目和大家见面!(请拭目以待呀~❤)
在「社区精选」栏目中,我们将为广大开发者推荐来自 SegmentFault 思否开发者社区的优质技术文章,这些文章全部出自社区中充满智慧的技术创作者哦!
希望通过这一栏目,大家可以共同学习技术干货,GET 新技能和各种花式技术小 Tips。
欢迎越来越多的开发者加入创作者的行列,我们将持续甄选出社区中优质的内容推介给更多人,让闪闪发光的技术创作者们走到聚光灯下,被更多人认识。
「社区精选」投稿邮箱:pr@segmentfault.com
投稿请附上社区文章地址
关注公众号:拾黑(shiheibook)了解更多
赞助链接:
关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 卢布汇率人民币2024年11月12日
- 微信小店助手APP上线:商家可在移动端管理店铺、查看数据
- 加元兑换人民币汇率2023年7月7日
- 直通 GDC 2023 中国行丨Azure OpenAI 服务改变游戏开发
- 历史上的今天:Apple II 问世;微软收购 GECAD;发明“软件工程”一词的科技先驱出生
- 恭喜你,现在可以在元宇宙里做 PPT 了
- 中国电信柯瑞文:推进更大范围共建共享,实现5G农村一张网
- 李佳琦618收官夜销售额超2亿;董明珠谈接班人;vivo将进军平板电脑市场|Do早报
- 这些交易,触目惊心!数十亿条个人信息明码标价售卖
- 一文读懂新型显示产业最新发展趋势
- 英特尔火线换帅,苹果搅动乾坤,国芯路在何方?
- 互联网如何反垄断(一):从四年的因到四个月的果