前端监控之rrweb录制用户行为
一、概述
rrweb 是 'record and replay the web' 的简写,旨在利用现代浏览器所提供的强大 API 录制并回放任意 web 界面中的用户操作。
1.1、为什么要录制用户行为?
前端录制用户行为能够提供有价值的数据,帮助团队从多个角度分析和优化产品,提升用户体验,解决潜在问题,最终促进产品的持续改进和业务的增长。
录制用户行为在前端监控中有以下几个重要原因:
用户体验优化。通过录制用户的行为,可以直观地看到用户在页面上的互动过程,帮助开发团队发现页面设计中的不合理之处,及时调整以提升用户体验。例如,某些页面功能可能导致用户困惑或卡顿,录制能帮助迅速识别并定位问题。
BUG排查和复现问题。当用户遇到某些问题时,直接复现问题往往较为困难。通过录制用户行为,可以重现用户的操作流程,帮助开发人员准确找到导致问题的步骤,从而更加高效地进行修复。
数据分析。通过分析录制的用户行为数据,能够了解用户的操作习惯、使用路径、停留时长等信息。这些数据可以帮助产品团队了解哪些功能是用户最常用的,哪些功能可能存在被忽视的情况,帮助产品优化和决策。
用户行为分析与热图。录制可以结合热图工具,帮助团队分析用户点击、滑动、浏览的区域,这样可以更好地优化页面布局、内容展示等,提高页面的转化率。
记录和回放。录制的行为可以在发生问题后进行回放,提供给QA或开发人员作为问题复现的依据,减少沟通成本。
安全和合规性。有些情况下,监控用户行为可以帮助发现潜在的安全问题,例如恶意操作、作弊行为等。通过记录用户的点击流,可以提供审计和追踪支持。
用户研究与市场调研。通过观察用户行为,能够深入了解用户的使用习惯和需求,为产品迭代和市场决策提供数据支持。
二、实践
实现技术环境Vite+Vue3+Pinia
2.1、安装rrweb
在前端项目目录下使用npm或pnpm(需要先安装pnpm)安装rrweb
# 安装rrweb
npm i rrweb --save
# 安装播放器
npm i rrweb-player --save
2.2、实现录制
创建store存储rrweb录制内容,以便回放
./store/rrweb/eventStore.ts
import { defineStore } from 'pinia';
import type { eventWithTime } from '@rrweb/types';
interface EventState {
events: eventWithTime[];
}
export const useEventStore = defineStore('eventStore', {
state: (): EventState => ({
events: []
}),
getters: {},
actions: {
setEvents(events: eventWithTime[]) {
this.events = events;
},
getEvents() {
return this.events;
}
}
}) as any;
记得在main.ts使用store
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
const app = createApp(App);
app.use(router);
app.use(store);
app.mount('#app');
新建录制操作页面
./views/rrweb/recordScreen.vue
<template>
<div id="record">
<div>
<el-button type="primary" @click="onStart">开始录制</el-button>
<el-button type="primary" @click="onStop">结束录制</el-button>
</div>
<el-form :model="form" label-width="auto" style="max-width: 600px">
<el-form-item label="Activity name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="Activity zone">
<el-select v-model="form.region" placeholder="please select your zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="Activity time">
<el-col :span="11">
<el-date-picker v-model="form.date1" type="date" placeholder="Pick a date" style="width: 100%" />
</el-col>
<el-col :span="2" class="text-center">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-time-picker v-model="form.date2" placeholder="Pick a time" style="width: 100%" />
</el-col>
</el-form-item>
<el-form-item label="Instant delivery">
<el-switch v-model="form.delivery" />
</el-form-item>
<el-form-item label="Activity type">
<el-checkbox-group v-model="form.type">
<el-checkbox value="Online activities" name="type"> Online activities </el-checkbox>
<el-checkbox value="Promotion activities" name="type"> Promotion activities </el-checkbox>
<el-checkbox value="Offline activities" name="type"> Offline activities </el-checkbox>
<el-checkbox value="Simple brand exposure" name="type"> Simple brand exposure </el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="Resources">
<el-radio-group v-model="form.resource">
<el-radio value="Sponsor">Sponsor</el-radio>
<el-radio value="Venue">Venue</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Activity form">
<el-input v-model="form.desc" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">Create</el-button>
<el-button>Cancel</el-button>
</el-form-item>
</el-form>
</div>
</template>
录制逻辑部分
<script lang="ts" setup>
import type { eventWithTime } from '@rrweb/types';
import * as rrweb from 'rrweb'; // 需要禁用浏览器扩展,否则会报错不可用
import { reactive } from 'vue';
import { useEventStore } from '@/store/rrweb/eventStore';
const eventStore = useEventStore();
let events: eventWithTime[] = [];
let stopFn: undefined | (() => void);
// do not use same name with ref
const form = reactive({
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
});
const onSubmit = () => {
console.log('submit!');
};
const onStart = () => {
stopFn = rrweb.record({
emit(event) {
console.log('event', JSON.stringify(event));
events.push(event);
},
recordCanvas: true
});
};
const onStop = () => {
if (!stopFn) return;
stopFn();
eventStore.setEvents(events);
};
</script>
2.3、实现回放
新建播放操作页面
./views/rrweb/playScreen.vue
<template>
<div class="play">
<div class="text-red font-800 h-30 text-center bg-blue-600">dddd</div>
<el-button type="primary" @click="onPlay">回放</el-button>
<hr />
<div id="doPlay"></div>
</div>
</template>
播放逻辑部分
<script setup lang="ts">
import rrwebPlayer from 'rrweb-player';
import 'rrweb-player/dist/style.css';
import { useEventStore } from '@/store/rrweb/eventStore';
const eventStore = useEventStore();
let events = eventStore.getEvents();
const onPlay = () => {
if (events.length === 0) return;
new rrwebPlayer({
target: document.querySelector('#doPlay')!,
props: {
events
}
});
};
</script>
2.4、解决问题
跳转测试rrweb页面失败报错
经过翻阅rrweb的github仓库issue,查明原因是有些浏览器插件会影响rrweb,先关掉所有插件
成功跳转rrweb录制页面
三、效果
点击开始录制,成功打印录制对象
结束录制,跳转播放页面播放录制
四、参考
rrweb/README.zh_CN.md at master · rrweb-io/rrweb