前端真的能“录屏”吗?我扒了 rrweb 的实现

146 阅读4分钟

我花 2 天把 rrweb 原理扒干净了,顺便讲清楚前端「录屏回放」怎么做

最近在看前端监控方案,绕不开 rrweb。
一开始以为只是“录个 DOM + 回放”,真翻完源码才发现,这玩意设计得很克制

iShot_2025-12-22_21.23.56 - 04.gif

这篇不是 API 教程,而是我理解 rrweb 之后,对它核心实现思路的一次拆解


先说结论:rrweb 干的不是“录屏”

如果你以为 rrweb 是:

把页面录成视频 → 出问题再回放

那就完全误会了。

rrweb 做的事情是:

记录页面结构 + 页面变化 + 用户操作,再按时间重放

所以它能做到:

  • 数据量可控
  • 可倍速 / 拖动
  • 精确复现 Bug

一、rrweb 的整体思路,其实很简单

我理解下来,rrweb 就三件事:

  1. 先拍一张“全家福” (初始 DOM 快照)
  2. 只记后面的变化(DOM / 输入 / 鼠标)
  3. 按时间一帧帧还原

结构大概是这样:

录制(record) → 事件流 → 回放(replay

看起来简单,但坑都在细节里。


二、为什么一定要「初始快照」

一开始我也疑惑:
既然能监听 DOM 变化,为什么不从空页面开始 replay?

后来发现不行。

原因很现实:

  • 回放时用户页面早没了
  • JS 不可重跑
  • 网络状态完全不同

所以 rrweb 的第一步一定是:

把当前页面完整“冻住”

但注意,它不是存 HTML 字符串,而是:

把 DOM 树序列化成 JSON

这一步,直接决定了后面所有设计。


三、rrweb 最关键的设计:NodeId

这是我觉得 rrweb 最聪明的地方。

❌ 为什么不用 DOM Path

html > body > div:nth-child(3) > span

这种路径有三个问题:

  • DOM 稍微一变就废
  • 定位成本高
  • 性能不友好

✅ rrweb 的做法:给每个节点发「身份证」

Node  ⇄  nodeId

一旦某个 DOM 被序列化过,它就有了一个全局唯一 id

之后所有事件,只认 id,不认 DOM。

这一步,直接把「回放稳定性」拉满。


四、DOM 变化到底怎么记?

rrweb 用的是 MutationObserver,但不会原样保存 mutation

它做的是一层“翻译”。

比如新增节点

rrweb 记录的是:

  • 父节点是谁
  • 插入了什么
  • 插在谁前面
{
  type: 'child-added',
  parentId,
  node,
  nextId
}

这样回放时,才能100% 还原位置


删除节点 / 属性变化也是同理

它存的是“语义”,不是浏览器原始事件。


五、一个非常容易踩的坑:input 事件

MutationObserver 监听不到

  • input.value
  • checkbox.checked

因为这些是 property,不是 attribute

所以 rrweb 会单独监听:

input / change / composition

并且还会:

  • mask 密码
  • 处理中文输入法
  • 区分 radio / checkbox

这一块不做全,回放基本就废了。


六、为什么 mousemove 不能原样记录?

这个很好理解:

mousemove 真的太多了。

如果你每一帧都记,一分钟就是几千条事件。

rrweb 的解法是:

  • 不记“每一次”
  • 只记“最近一次”
  • 固定频率采样

这样数据量直接降一个数量级。


七、回放为什么一定要用 iframe?

这个点我一开始没想明白,后来才意识到很重要。

用 iframe 能解决的事情包括:

  • CSS 完全隔离
  • position: fixed 正确
  • 不污染宿主页面
  • 不执行原页面 JS

一句话总结:

rrweb 回放的是页面“状态”,不是页面“逻辑”


八、rrweb 的回放不是 setTimeout

如果用 setTimeout,你会很快发现:

  • 暂停难
  • 拖动时间轴几乎不可能

rrweb 用的是:

requestAnimationFrame + 时间轴推进

本质是:

当前时间能执行哪些事件,就执行哪些

这也是它能支持倍速和拖动的原因。


九、rrweb 最核心的一条原则

看完源码我最大的感受是:

rrweb 非常不信任浏览器

所以它:

  • 不执行 script
  • 不触发真实事件
  • 所有变化都由 replay engine 控制

也正因为这样,回放结果才稳定。


十、rrweb 在真实项目里的用法

一般不会全程录:

  • 错误前 N 秒开始
  • 出错立即停止
  • 只上传关键片段

配合:

  • error stack
  • 用户环境
  • 性能数据

定位问题效率会直接拉满。


最后一点个人感受

rrweb 并不复杂,但非常工程化

它的厉害之处不是炫技,而是:

  • 每一步都在权衡成本
  • 每一个设计都有明确取舍

如果你在做:

  • 前端监控
  • 复杂交互系统
  • 编辑器 / 可回放系统

rrweb 这套思路,真的值得完整读一遍。