使用rrweb进行前端页面可回溯录制

打印 上一主题 下一主题

主题 834|帖子 834|积分 2502

配景

公司需要对用户的页面交互进行可回溯记录,固使用rrweb进行需求实现;
先容

rrweb 全称 'record and replay the web',是当下很流行的一个录制屏幕的开源库。与我们传统认知的录屏方式(如 WebRTC)不同的是,rrweb 录制的不是真正的视频流,而是一个记录页面 DOM 变革的 JSON 数组,因此不能录制整个显示器的屏幕,只能录制欣赏器的一个页签。
   rrweb的github地址:rrweb/guide.zh_CN.md at master · rrweb-io/rrweb · GitHub
  功能实现



  • 首先安装rrweb和rrweb-player
  1. npm install --save rrweb
  2. npm install --save rrweb-player
复制代码


  • 创建screen-record.js文件对上报方法进行封装
rrweb 提供了一个基于 fflate 的简单压缩函数,在提交录制中可以作为 packFn 传入使用。
可以将录制流程分为不同的节点(stepIndex),与服务端商量好进行时间轴排序存储
  1. import { record, pack, unpack } from 'rrweb'
  2. import { saveTCDataByXhr, saveTCDataByBeacon, getTcKey, saveTCDataByFetch } from '@/api/record'
  3. /**
  4. * 开始屏幕录制
  5. */
  6. export function startRecord() {
  7.   if (typeof window.stopRecordFn === 'function') {
  8.     window.stopRecordFn()
  9.   }
  10.   window.screenRecords = []
  11.   console.log('=========== Record Start ==========')
  12.   const stopFn = record({
  13.     emit(event) {
  14.       window.screenRecords.push(event)
  15.     },
  16.     packFn: pack, // rrweb 内包含了基于 fflate 的简单压缩 rrweb.pack,在录制时可以作为 packFn 传入。
  17.     inlineStylesheet: false,
  18.     sampling: {
  19.       scroll: 150, // 每 150ms 最多触发一次
  20.       // set the interval of media interaction event
  21.       media: 800,
  22.       // input: 'last' // 连续输入时,只录制最终值
  23.     },
  24.   })
  25.   window.stopRecordFn = stopFn
  26. }
复制代码


  •  提交录制数据
由于一般提交页面录制数据的时机为页面离开的时机,当欣赏器中的某个页面发生停止时,不能包管进程中的HTTP请求会成功(请参阅有关“停止”和页面生命周期的其他状态的更多信息)。以是我们有如下几个解决方法:

  • 使用async/await阻塞异步请求,直至请求发送完毕再进行路由跳转,但似乎这种方法牺牲了用户体验;
  • 使用fetch请求的keepalive标识进行数据提交,但是会有欣赏器兼容性题目,其兼容性如下图:

  • 使用Navigator.sendBeacon(),该函数专门用于发送单向请求(信标)。但是此 API 不答应您发送自定义标头。因此,为了让我们以“application/json”的情势发送数据,我们需要做一些小调整并使用Blob:
    1. /**
    2. * beacon上传可回溯录制数据
    3. */
    4. export function saveTCDataByBeacon(data) {
    5.   const url = `${RECORD_BASE_API}/tc/groOrder/saveTcData`
    6.   // let data = new FormData();
    7.   // for (let key in params) {
    8.   //     data.append(key, params[key]);
    9.   // }
    10.   const headers = {
    11.     type: 'application/json'
    12.   }
    13.   const blob = new Blob([JSON.stringify(data)], headers)
    14.   return navigator.sendBeacon(url, blob)
    15. }
    复制代码
    但是注意:beacon只能提交少量数据,chrome限定最高64KB
基于上诉思路我们封装提交录制方法:
  1. /**
  2. *
  3. * @param stepIndex 录制节点 立即投保: 100, 暂存: 200, 提交审核: 300, 支付: 400, 支付成功: 500
  4. * @param goodsCode
  5. * @param submitType 提交数据的方式 默认xhr,页面返回beacon
  6. */
  7. export async function submitRecord(stepIndex, linkNo, submitType = 'xhr') {
  8.   if (!window.screenRecords || !window.screenRecords.length) return
  9.   const startEvent = unpack(window.screenRecords[0])
  10.   const endEvent = unpack(window.screenRecords[window.screenRecords.length - 1])
  11.   const { data: tcKey } = await getTcKey({ linkNo })
  12.   const dataString = JSON.stringify(window.screenRecords)
  13.   window.screenRecords.length = 0
  14.   const params = {
  15.     linkNo,
  16.     tcKey,
  17.     stepIndex,
  18.     startTime: startEvent.timestamp,
  19.     endTime: endEvent.timestamp,
  20.     data: dataString,
  21.   }
  22.   // if (stepIndex >= 60) {
  23.   //   params.linkNo = linkNo
  24.   //   delete params.tcKey
  25.   // }
  26.   return new Promise((reslove, reject) => {
  27.     // @ts-ignore
  28.     if (navigator && navigator.sendBeacon && submitType === 'beacon') {
  29.       // beacon只能提交少量数据,chrome限制最高64KB
  30.       const result = saveTCDataByBeacon(params)
  31.       if (result) {
  32.         console.log('回溯数据请求成功排队 等待执行')
  33.         reslove()
  34.       } else {
  35.         console.log('回溯数据提交失败')
  36.         reject('回溯数据提交失败')
  37.       }
  38.     } else if (submitType === 'fetch') {
  39.       saveTCDataByFetch(params).then(() => {
  40.         reslove()
  41.       }).catch(e => reject(e))
  42.     } else {
  43.       saveTCDataByXhr(params).then(() => {
  44.         reslove()
  45.       }).catch(e => reject(e))
  46.     }
  47.   })
  48. }
复制代码
  1. /**
  2. * xhr上传可回溯录制数据
  3. */
  4. export function saveTCDataByXhr(data) {
  5.   return request({
  6.     url: `${RECORD_BASE_API}/tc/groOrder/saveTcData`,
  7.     method: 'post',
  8.     data,
  9.   })
  10. }
  11. /**
  12. * Fetch上传可回溯录制数据 将 keepalive 设置为 true 就可确保浏览器关闭或回退,调用接口的链接不会被关闭,调用成功
  13. */
  14. export function saveTCDataByFetch(data) {
  15.   const url = `${RECORD_BASE_API}/tc/groOrder/saveTcData`
  16.   const headers = {
  17.     'Content-Type': 'application/json;charset=UTF-8',
  18.     [REQUEST_TOKEN_KEY]: getToken()
  19.   }
  20.   return new Promise((resolve, reject) => {
  21.     fetch(url, {
  22.       method: 'POST',
  23.       headers,
  24.       body: JSON.stringify(data),
  25.       // keepalive: true,
  26.     })
  27.       .then(res => {
  28.         resolve(res.json())
  29.       })
  30.       .catch(error => {
  31.         reject(error)
  32.       })
  33.   })
  34. }
复制代码
页面使用

开始录制:
  1. startRecord()
复制代码
提交录制数据:
  1. submitRecord(300, this.baseInfo.grpLinkNo)
复制代码
页面回放:
  1. </template>   
  2.   <!-- 回放容器 -->
  3.   <div ref="replayContainer" class="replay-container flex justify-center" />
  4. </template>
  5. <script setup>
  6.   import { ref, onMounted} from 'vue';
  7.   import RrwebPlayer from 'rrweb-player';
  8.   import { unpack } from 'rrweb';
  9.   const replayContainer = ref();
  10.   const events = ref([]) // 回放数据通过服务端获取
  11.   
  12.   onMounted(() => {
  13.      new RrwebPlayer({
  14.          target: replayContainer.value,
  15.          unpackFn: unpack,
  16.          props: {
  17.             events: events, // 包含回放所需的数据
  18.             skipInactive: true, // 是否快速跳过无用户操作的阶段
  19.             autoPlay: false, // 是否自动播放
  20.             UNSAFE_replayCanvas: true, // 回放时是否回放 canvas 内容,开启后将会关闭沙盒策略,导致一定风险
  21.             mouseTail: false, // 是否在回放时增加鼠标轨迹
  22.           },
  23.         })
  24.      })
  25.   })
  26. </script>
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曹旭辉

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表