前端监控之rrweb录制用户行为

一、概述

rrweb 是 'record and replay the web' 的简写,旨在利用现代浏览器所提供的强大 API 录制并回放任意 web 界面中的用户操作。

1.1、为什么要录制用户行为?

前端录制用户行为能够提供有价值的数据,帮助团队从多个角度分析和优化产品,提升用户体验,解决潜在问题,最终促进产品的持续改进和业务的增长。

录制用户行为在前端监控中有以下几个重要原因:

用户体验优化。通过录制用户的行为,可以直观地看到用户在页面上的互动过程,帮助开发团队发现页面设计中的不合理之处,及时调整以提升用户体验。例如,某些页面功能可能导致用户困惑或卡顿,录制能帮助迅速识别并定位问题。

BUG排查和复现问题。当用户遇到某些问题时,直接复现问题往往较为困难。通过录制用户行为,可以重现用户的操作流程,帮助开发人员准确找到导致问题的步骤,从而更加高效地进行修复。

数据分析。通过分析录制的用户行为数据,能够了解用户的操作习惯、使用路径、停留时长等信息。这些数据可以帮助产品团队了解哪些功能是用户最常用的,哪些功能可能存在被忽视的情况,帮助产品优化和决策。

用户行为分析与热图。录制可以结合热图工具,帮助团队分析用户点击、滑动、浏览的区域,这样可以更好地优化页面布局、内容展示等,提高页面的转化率。

记录和回放。录制的行为可以在发生问题后进行回放,提供给QA或开发人员作为问题复现的依据,减少沟通成本。

安全和合规性。有些情况下,监控用户行为可以帮助发现潜在的安全问题,例如恶意操作、作弊行为等。通过记录用户的点击流,可以提供审计和追踪支持。

用户研究与市场调研。通过观察用户行为,能够深入了解用户的使用习惯和需求,为产品迭代和市场决策提供数据支持。

二、实践

实现技术环境Vite+Vue3+Pinia

2.1、安装rrweb

在前端项目目录下使用npm或pnpm(需要先安装pnpm)安装rrweb

bash 复制代码
# 安装rrweb
npm i rrweb --save
# 安装播放器
npm i rrweb-player --save

2.2、实现录制

创建store存储rrweb录制内容,以便回放

./store/rrweb/eventStore.ts

javascript 复制代码
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

javascript 复制代码
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

html 复制代码
<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>

录制逻辑部分

html 复制代码
<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

html 复制代码
<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>

播放逻辑部分

html 复制代码
<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

相关推荐
yinxiangzhongqing2 分钟前
loadash知识整理
前端·javascript·chrome
德莱厄斯21 分钟前
三行代码完成国际化适配,妙~啊~
前端·javascript·babel
2301_7891695424 分钟前
JSON.parse(JSON.stringify())深拷贝不会复制函数
开发语言·前端·javascript
程序员XC27 分钟前
前端性能优化的思考过程
前端·javascript·面试
皮皮虾我们跑1 小时前
web—HTML
前端·html
JosieBook1 小时前
【前端】如何在HTML中调用CSS和JavaScript(完整指南)
前端·css·html
唐诗1 小时前
这位同学来说一说 vue3 的组件通信
前端
积跬步,慕至千里1 小时前
pyspark RDD相关常用函数使用案例
前端·javascript·ajax
星星不打輰2 小时前
Vue脚手架基础
前端·javascript·vue.js