@rrweb/record 记录包全解:从 2.0 重大变更到 2.1 性能优化的演进指南
【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrweb
@rrweb/record是 rrweb 生态中专用于"录制端"的独立 npm 包,面向需要在浏览器中采集页面事件的前端应用。本文以该包的 CHANGELOG.md 为主线,结合包内源码与测试,逐版本解读从 2.0.0 到 2.1.5 的关键变更——包括破坏性的产物命名与 UMD 全局名调整、打包体积治理、录制性能优化等,并给出可直接落地的安装、引入与迁移方案。读完本文,你将掌握@rrweb/record的包结构与发布节奏,理解每次升级对既有接入代码的实际影响,并能写出与 2.x 版本兼容的录制接入代码。
一、包定位:录制逻辑的唯一出口
@rrweb/record在 rrweb 生态中的职责非常清晰:只暴露录制能力。它的核心实现其实是一个极简包装层:
// packages/record/src/index.ts import { record } from 'rrweb'; export { record };从 src/index.ts 可以看到,包本身只是把主rrweb包中的record函数重新导出(re-export)。在 packages/record/README.md 中作者也明确说明:"Currently this package is really just a wrapper around therecordfunction in the mainrrwebpackage. Allrecordrelated code will get moved here in the future."(当前该包只是主包record函数的包装,未来所有录制相关代码会迁移到这里。)
因此,@rrweb/record承担的是一个"面向未来"的包边界:对使用方来说,它提供了稳定的录制 API 入口;对项目来说,它把录制代码与回放代码在包级别彻底隔离,为后续 tree-shaking(摇树优化)与按需加载奠定基础。
record函数的真实实现在主包中,位于 packages/rrweb/src/record/index.ts,其签名结构为:
function record<T = eventWithTime>( options: recordOptions<T> = {}, ): listenerHandler | undefined { const { emit, checkoutEveryNms, // ... 其余录制选项 } = options; // ... return stopHandler; // 返回用于停止录制的函数 }它接收可选的recordOptions<T>配置对象,返回一个"停止录制"的监听处理器(listenerHandler),并且record.mirror = mirror挂载了内部节点镜像。完整的recordOptions说明可参见仓库根目录的 guide.md 中的record-options一节。
二、安装与三种引入方式
根据 package.json,@rrweb/record当前版本为2.1.5,声明了如下依赖:
{ "dependencies": { "@rrweb/types": "^2.1.5", "rrweb": "^2.1.5", "@rrweb/utils": "^2.1.5" } }包的类型声明位于dist/index.d.ts,浏览器兼容目标为supports es6-class(支持 ES6 class 的现代浏览器)。
方式一:通过 npm / bundler 引入(推荐)
npm install @rrweb/recordimport { record } from '@rrweb/record'; record({ emit(event) { // 将事件发送到服务端 }, });这是 README.md 推荐的接入方式,构建产物兼容现代浏览器、Node.js 以及支持 ES Modules 的打包器。
方式二:浏览器直接加载(ESM)
不使用打包器时,可以直接在浏览器中以 ES Module 方式加载 CDN 资源:
<script type="module"> import { record } from 'https://cdn.rrweb.com/record/current/dist/record.js'; </script>其中current指向最新稳定版;生产环境建议锁定具体版本以保证不可变 URL,例如:
<script type="module"> import { record } from 'https://cdn.rrweb.com/record/2.0.0/dist/record.js'; </script>方式三:传统<script>直接引入(UMD 兜底)
仅用于不支持 ES Module 的旧环境:
<script src="https://cdn.rrweb.com/record/current/dist/record.umd.cjs"></script>注意:此方式暴露的全局变量名为rrwebRecord(而非旧版的rrweb),这正是 2.0.0 版本的一项破坏性变更,详见下文。
三、2.0.0 重大变更:升级前必须了解的三件事
2.0.0 是该包历史上最大的一次重构(对应 CHANGELOG.md 中的2.0.0一节),涉及三个破坏性变更与一批功能性补丁。如果你正在从 1.x 升级,以下内容直接决定你的代码是否需要改动。
3.1 UMD 全局名从rrweb改为rrwebRecord
为避免录制器与回放器同时加载在同一页面时发生全局命名冲突,2.0.0 将 UMD 全局名拆分:
- 录制器(recorder)全局名:
rrwebRecord - 回放器(replayer)全局名:
rrwebReplay
这意味着所有通过<script>标签直接引用 UMD 产物的老代码,都需要把全局变量的引用从rrweb改为rrwebRecord。
3.2 分发产物文件名、路径与扩展名全面调整
2.0.0 重做了构建产物规范(对应 PR #1497),核心变化如下:
- 所有
.js文件现在都是ES Modules,可用于现代浏览器、Node.js 以及支持 ESM 的打包器; - 所有 npm 包同时附带
.cjs与.umd.cjs文件:.umd.cjs:CommonJS 格式且内联打包所有依赖,便于在浏览器环境用一个文件直接引入(类似旧版.js文件);.cjs:CommonJS 格式,用于较老的 Node.js 环境;
- 新增
/umd/输出目录,与/dist/并存,从而可以以.js扩展名提供 UMD 文件,而不破坏 package.json 中"/dist/下所有.js均为模块"的约定; - 类型导出更规范:如果需要特定类型(例如
PlayerMachineState、SpeedMachineState),它们现在从@rrweb/replay等新包导出;具体可用文件以各包package.json的main与exports字段为准。
从当前 package.json 可以直观看到这套新规范:
{ "type": "module", "main": "./dist/record.cjs", "module": "./dist/record.js", "unpkg": "./dist/record.umd.cjs", "jsdelivr": "./umd/record.js", "typings": "dist/index.d.ts", "exports": { ".": { "import": { "types": "./dist/index.d.ts", "default": "./dist/record.js" }, "require": { "types": "./dist/index.d.cts", "default": "./dist/record.cjs" } } }, "files": ["umd", "dist", "package.json"] }迁移建议:如果你的代码通过import rrweb from 'rrweb'方式使用,则本次变更对你几乎无感;但如果你直接引用了分发文件(例如rrweb/typings/...、rrdom/es),或在<script>标签中直接引入旧版rrweb-all.js、rrweb-record.js、rrweb-replay.js,则必须更新路径——改为引用.umd.cjs文件,或改用新包。
3.3 移除rrweb-all.js/rrweb-record.js/rrweb-replay.js
这三个文件从rrweb主包中彻底移除,取而代之的是按职责拆分的独立包:
@rrweb/all:聚合导出(录制 + 回放 + 打包器)@rrweb/record:仅录制@rrweb/replay:仅回放
从 packages/all/src/index.ts 可以看到@rrweb/all的聚合方式:
export * from 'rrweb'; export * from '@rrweb/packer'; import 'rrweb/dist/style.css';它重新导出主包全部 API 与打包器,并附带引入回放所需的样式文件。如果你以前用rrweb-all.js一把梭,现在应当按需拆分引入,只加载自己需要的部分。
3.4 2.0.0 的功能性补丁
除破坏性变更外,2.0.0 还包含一系列稳定性与兼容性修复:
| 变更点 | 说明 |
|---|---|
| 移除各 bundle 中 base64 内联的 worker 源码 | 减小产物体积,worker 改为独立文件加载 |
支持已废弃的addRule/removeRule方法 | 兼容仍在使用旧 CSSOM API 的页面(PR #1515) |
捕获WebGLRenderingContext前先校验其是否存在 | 避免在不支持 WebGL 的环境抛错(PR #1777) |
patch函数迁移至@rrweb/utils | 提升打包复用性,减少重复代码(PR #1631) |
| 正确识别 Angular 包装后的 MutationObserver | 修复 Angular 框架下录制失效的问题(PR #1597) |
从@rrweb/recordbundle 中摇树掉回放专用的postcss代码 | 录制包不再携带回放才需要的 CSS 处理逻辑(PR #1837) |
新增/umd/输出目录 | 见 3.2 节说明 |
四、打包体积治理:一条有测试约束的硬红线
@rrweb/record的体积不是靠自觉维护的,而是被自动化测试强制约束。在 packages/record/test/record.test.ts 中可以看到两个关键断言:
// 修复前 ESM bundle 大小:397373 字节 // 修复后 ESM bundle 大小:161287 字节 // 修复后的 ESM bundle 必须比基线至少小 200 KiB const BASELINE_RECORD_JS_BYTES = 397373; const MAX_RECORD_JS_BYTES = BASELINE_RECORD_JS_BYTES - 200 * 1024;测试逻辑分三层:
- 导出可用性:
typeof record === 'function',保证 API 形态稳定; - 无回放代码泄漏:遍历
dist/下所有.js/.cjs产物,断言内容中不包含postcss字样——这就是 3.4 节"摇树掉回放专用 postcss"的回归防线,防止未来重构又把回放依赖带进录制包; - 体积上限:
dist/record.js的大小必须小于等于397373 - 200 * 1024 ≈ 192765字节。实测摇树修复后为 161287 字节,比 2.0.0 基线瘦身约 236 KB(近 60%)。
这一设计思路值得借鉴:把"体积"当作与"功能正确性"同等重要的非功能需求,用单测持续守护。也正因为此,@rrweb/record才能以极小的包体承载完整录制能力。
五、2.1.x 增量演进:性能与健壮性打磨
进入 2.1 系列后,@rrweb/record没有再做破坏性变更,而是聚焦性能与细节。逐版本梳理如下:
2.1.5:录制热路径性能优化
这是 2.1 系列最重要的一次性能更新,包含两点:
- 未受污染的 DOM 访问器性能提升:自 #1509 起,为绕开某些库对
parentNode等访问器的篡改,代码改为使用dom.parentNode(el)这类封装调用。2.1.5 进一步优化,避免每次调用时分配字符串——这些调用位于所有录制热路径上,字符串分配开销会被放大到每次 DOM 变更; - 录制期 mutation 处理效率提升:重排了 mutation 处理顺序,录制效率显著改善,同时新的 mutation 排序还带来更快的回放性能。该问题此前被多位社区成员反复报告,是录制侧公认的痛点。
对应地,packages/rrweb/src/record/下的 mutation.ts 与 observer.ts 就是这些优化的落点所在——DOM 变更观察与序列化在每次用户交互时都会被触发,属于典型的热路径。
2.1.0:绝对 URL 转相对 URL 的 hash 修复
修复了一个边界 bug:当 URL 中包含 hash(#...)时,绝对 URL 转相对 URL 的转换逻辑会产生错误结果。这影响的是录制事件中资源 URL 的归一化处理,修复后带 hash 的链接也能被正确转为相对地址。
2.1.1 / 2.1.2 / 2.1.3 / 2.1.4:纯依赖同步
这几个版本没有@rrweb/record自身的代码变更,只是随主包与类型包一起同步版本(rrweb、@rrweb/types、@rrweb/utils同版本对齐)。这正是 rrweb monorepo 采用的changesets 统一版本管理策略:所有包保持相同版本号,通过turbo工作流协调构建与发布。
2.0.1:补丁同步
同样为依赖同步版本,未包含本包独立变更。
六、版本发布节奏与全版本一览
@rrweb/record的发布遵循 changesets 规范:所有相关包(rrweb、@rrweb/types、@rrweb/utils)保持版本同步。从 CHANGELOG.md 整理的完整版本时间线如下:
| 版本 | 类型 | 核心内容 |
|---|---|---|
| 2.1.5 | Patch | 未污染 DOM 访问器性能优化;mutation 处理效率提升 |
| 2.1.4 | Patch | 依赖同步 |
| 2.1.3 | Patch | 依赖同步 |
| 2.1.2 | Patch | 依赖同步 |
| 2.1.1 | Patch | 依赖同步 |
| 2.1.0 | Patch | 修复含 hash 的绝对 URL 转相对 URL |
| 2.0.1 | Patch | 依赖同步 |
| 2.0.0 | Major | UMD 全局名改名;产物路径/命名/扩展名重构;移除rrweb-*.js独立文件;若干兼容性补丁 |
| 2.0.0-alpha.15 ~ 2.0.0-alpha.20 | Major/Alpha | 2.0.0 的预发布迭代(含addRule/removeRule支持、patch函数迁移等) |
需要注意的是,2.0.0-alpha.x 系列属于预发布版本,其中出现的变更(如db20184"保持包版本与其他包同步")最终都汇总进了 2.0.0 正式版。
七、包内工程配置速查
如果你要本地开发或调试@rrweb/record,package.json 中几个常用脚本值得关注:
{ "scripts": { "dev": "vite build --watch", "build": "yarn turbo run prepublish", "test": "yarn build && vitest run", "test:watch": "vitest watch", "check-types": "tsc -noEmit", "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts" } }yarn workspace @rrweb/record build:通过 turbo 触发prepublish,先做类型检查再走 vite 构建(体积测试要求先执行此步,否则 record.test.ts 会因缺少dist/record.js而报错);yarn workspace @rrweb/record test:构建后运行 vitest,即上文所述的三项体积/内容断言;- 构建工具链:vite + vite-plugin-dts(生成
.d.ts类型)、vitest(单测)、puppeteer(浏览器相关测试)。
八、迁移检查清单
综合以上分析,从旧版本升级到@rrweb/record2.x 时,建议逐项核对:
- 全局变量引用:UMD 方式使用的全局名是否已从
rrweb改为rrwebRecord; - 脚本标签路径:直接引入的
rrweb-record.js等文件路径是否已替换为.umd.cjs或新包 CDN 地址; - 产物导入:若通过
import使用,确认打包器正确解析exports字段(import走dist/record.js,require走dist/record.cjs); - 类型引用:需要回放侧状态机类型(如
PlayerMachineState)时,改从@rrweb/replay导入; - 浏览器兼容:包目标环境为
supports es6-class,不支持 ES6 class 的老浏览器需自行引入 polyfill 或使用 UMD 产物; - 体积预期:ESM 产物约 161 KB(摇树修复后),若接入方同时打包主包,可进一步通过 tree-shaking 获益——录制场景应优先选择
@rrweb/record而非@rrweb/all,以规避回放专用代码(如 postcss)被带入产物。
结语
@rrweb/record虽然当前仍以"薄包装"形态存在,但它的出现标志着 rrweb 在包边界、构建规范与体积治理上的系统性演进:2.0.0 用一套破坏性变更换来了清晰的模块划分与现代化的产物矩阵,2.1.x 则把优化火力集中在录制热路径的性能上,并用自动化测试锁住"录制包绝不携带回放代码"的底线。对于接入方而言,理解 CHANGELOG.md 中的每一次版本变化,就是理解录制端 API 稳定性与性能边界的最佳入口——从 2.1.x 开始,你可以放心地把record作为长期稳定的录制入口来依赖。
【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrweb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考