- 前端
- 可观测性
- 开发工具
【免费下载链接】rrweb
record and replay the web
@rrweb/types是 rrweb 在 2.0 时代从核心仓库中拆分出的共享类型包,集中定义了录制与回放所依赖的事件契约、增量数据类型与插件接口。本篇文章以 packages/types/CHANGELOG.md 为主线,结合仓库源码,系统梳理该包的诞生背景、事件契约全景、2.0 大版本的破坏性变更,以及 2.1.x 阶段的关键能力演进,帮助读者理解 rrweb 事件格式的来龙去脉,并掌握升级与迁移的要点。
一、包的定位:为什么需要一个独立的共享类型包
在 rrweb 2.0 之前,事件类型(event types)与录制类型(recorder types)散落在各个包中,跨包引用类型时常常需要深挖内部路径。2.0 版本通过 PR #1031(对应 changelog 2.0.0 的 Major Changes)将共享的 rrweb 事件类型和录制类型集中迁移到新的@rrweb/types包中,从而:
- 让
rrweb、rrweb-snapshot、rrweb-replay、rrdom以及各类插件包都能从同一处导入类型,避免重复定义和循环依赖; - 为第三方插件作者提供稳定的类型契约(如
RecordPlugin、各增量源的数据结构); - 让类型与运行时代码解耦,
@rrweb/types本身几乎不包含运行时逻辑,从 packages/types/src/index.ts 的源码结构看,该文件从头到尾都是类型与枚举的导出,这也解释了为什么它可以在type: "module"下以极小的体积被任何包引用。
从仓库的依赖关系可以印证这一地位:rrweb、rrweb-snapshot、rrweb-replay、rrdom、rrdom-nodejs、packer、browser-client、all以及网络插件包均在其 package.json 中以"@rrweb/types": "^2.1.5"或相近版本声明依赖。而在 packages/rrweb/src/index.ts 中,主包会从@rrweb/types导入并重新导出大量公开类型,使用者通过import rrweb from 'rrweb'即可获得类型与实现,无需单独安装类型包即可获得类型提示。
二、事件类型契约全景:从源码看 types 包的核心资产
@rrweb/types的核心价值在于它完整定义了 rrweb 的事件序列化格式。以当前仓库 packages/types/src/index.ts 为证,契约主要分为以下几层。
1. 顶层事件枚举与事件结构
EventType枚举(packages/types/src/index.ts#L1-L10)定义了八类顶层事件:
export enum EventType { DomContentLoaded, Load, FullSnapshot, IncrementalSnapshot, Meta, Custom, Plugin, Asset, }每类事件对应一个带type判别字段的结构体,最终通过eventWithoutTime联合类型统一,再叠加timestamp与可选delay形成带时间戳的eventWithTime(packages/types/src/index.ts#L247-L250):
export type eventWithTime = eventWithoutTime & { timestamp: number; delay?: number; };eventWithTime是录制端输出的标准单位,也是回放端、@rrweb/packer(见 packages/packer/src/pack.ts 的PackFn = (event: eventWithTime) => string)与存储层共同依赖的数据形状。
2. 增量事件源:IncrementalSource 与 incrementalData
页面交互过程中的动态变化通过IncrementalSnapshot事件承载,其data由incrementalData联合类型定义(packages/types/src/index.ts#L215-L229),包含 15 种来源,枚举IncrementalSource(packages/types/src/index.ts#L134-L152)覆盖了 DOM 变更、鼠标/触摸/拖拽、滚动、视口缩放、输入、媒体交互、样式表规则与声明、Canvas 变更、字体、选区、AdoptedStyleSheet、自定义元素等全部录制维度。每种来源都定义了独立的xxxData结构,例如:
mutationData(DOM 增删改)mouseInteractionData(点击、聚焦、上下文菜单等)mediaInteractionData(播放、暂停、音量、倍速等)canvasMutationData(2D/WebGL 绘制调用)styleSheetRuleData/styleDeclarationData(CSSOM 变更)
3. 录制回调与采样配置
hooksParam(packages/types/src/index.ts#L330-L344)定义了录制端向外部暴露的各类回调钩子;SamplingStrategy(packages/types/src/index.ts#L261-L295)则规定了采样策略的类型形状(如mousemove的节流阈值、input: 'all' | 'last'、canvas: 'all' | number等),是rrweb.record()配置项的类型来源。这些类型正是 2.0 拆分时从主包迁入@rrweb/types的"录制类型"主体。
三、2.0.0 大版本:破坏性变更与产物分发体系重构
changelog 的 2.0.0 条目集中记录了两个 Major Changes,这是理解该包(乃至整个 rrweb 2.0 生态)升级成本的关键。
1. 产物文件命名、路径与扩展名全面调整(PR #1497)
这是 rrweb 2.0 最容易被忽略的破坏性变更:分布式文件的文件名、路径和扩展名都变了。changelog 原文明确提示:
- 如果直接引用分布式文件或类型,必须更新路径/文件名,例如从
rrweb/typings/...或rrdom/es导入的写法不再有效; - 若通过
import rrweb from 'rrweb'使用,则感知不到这一变化; - 若通过
<script>标签直接引入,需要改用.umd.cjs文件; - 所有
.js文件现在都是 ES 模块,适用于现代浏览器、Node.js 及支持 ESM 的打包器; - 所有 npm 包同时提供
.cjs与.umd.cjs:.umd.cjs是打包为单文件的 CommonJS 产物(类似旧版.js的浏览器用法);.cjs供旧版 Node.js 使用。
从当前 packages/types/package.json 可以清晰看到这套体系的落地形态:
{ "type": "module", "main": "./dist/types.umd.cjs", "module": "./dist/types.js", "unpkg": "./dist/types.umd.cjs", "jsdelivr": "./umd/types.js", "typings": "dist/index.d.ts", "exports": { ".": { "import": { "types": "./dist/index.d.ts", "default": "./dist/types.js" }, "require": { "types": "./dist/index.d.cts", "default": "./dist/types.umd.cjs" } } }, "files": ["umd", "dist", "package.json"] }要点解读:
import条件对应 ESM 产物dist/types.js,require条件对应 CommonJS 单文件产物dist/types.umd.cjs,并分别提供.d.ts与.d.cts类型声明;exports字段锁定了包的唯一入口,禁止深路径导入(如@rrweb/types/dist/xxx),这也是 PR #1497 所说的"package.json 的main和exports字段决定了可用文件";files只发布umd、dist与package.json,其中umd目录是 PR #1704 补充的:在dist之外额外提供带.js扩展名的 UMD 文件(jsdelivr指向./umd/types.js),避免"package.json 声明dist下所有.js都是模块"的预期被 UMD 文件破坏。
升级提示:如果你的构建链路或 CDN 直接引用了旧路径(如rrweb/typings/...),升级 2.0 后必须按上述exports映射调整;如果只是import rrweb from 'rrweb',则无需改动。
2. 类型归属调整:特定类型迁移到其他包(PR #1031 / #1497)
changelog 明确提到,部分特定类型被导出到新的包中,例如PlayerMachineState与SpeedMachineState现在从@rrweb/replay导出。这意味着:
- 回放相关状态机类型不再属于
@rrweb/types,需要从回放包引入; - 使用
@rrweb/types时应只引用事件与录制契约相关类型,避免继续依赖旧的集中式 typings 入口。
3. 工程细节:TypeScript 4.9.5 与 NodeNext 兼容(PR #1287 / #1369)
- PR #1287 将仓库全部项目升级到 TypeScript 4.9.5,
@rrweb/types的类型声明也随之上限对齐; - PR #1369 修复了
"moduleResolution": "NodeNext"下的类型错误,这一点对使用 Node.js 原生 ESM 解析策略(NodeNext/Node16)的消费者尤为重要——如果升级后出现类型解析错误,请检查是否已使用包含exports字段映射的较新版本(2.0.0 正式版起已修复)。
四、事件契约的关键能力演进(2.0 系列 Minor 变更)
除了破坏性重构,2.0 阶段还通过多个 Minor 变更扩充了事件契约本身,这些都可以在当前源码中得到印证。
1. pointerType:区分鼠标、触控笔与触摸(PR #1129)
点击类事件新增.pointerType属性,用于区分pen、mouse、touch三类指针来源(对应 PointerEvent.pointerType 的取值)。changelog 特别说明:没有新增 PenDown/PenUp 事件,笔事件可以通过MouseDown/MouseUp + pointerType=pen组合识别。
在类型层,packages/types/src/index.ts#L446-L450 定义了PointerTypes枚举(Mouse、Pen、Touch),而mouseInteractionParam(packages/types/src/index.ts#L496-L502)将pointerType?: PointerTypes声明为可选字段,以保持向后兼容。
在实现层,packages/rrweb/src/record/observer.ts#L219-L275 展示了完整的映射逻辑:从原生事件读取pointerType字符串,映射到PointerTypes枚举;对触摸事件还会维护"当前指针类型"状态,在后续移动/交互事件中沿用该状态,最后仅在pointerType !== null时写入事件对象。这条链路说明:录制端生成的增量事件中,pointerType是"可选但尽量填充"的增强字段,回放端消费时需做空值兼容。
2. 顶层<dialog>组件支持(PR #1503)
PR #1503 为顶层<dialog>组件提供支持,修复了 #1381。其类型落点在快照侧: packages/rrweb-snapshot/src/types.ts#L7-L22 定义了DialogAttributes:
export type DialogAttributes = { open: string; rr_open_mode: 'modal' | 'non-modal'; // rr_open_mode_index?: number; // 预留,用于按顺序回放多次 showModal() };rr_open_mode区分showModal()(modal)与show()/添加open属性(non-modal)两种打开方式;注释中还预留了rr_open_mode_index字段,用于未来按打开顺序回放多个 dialog。这与仓库中 dialog 相关的回放测试(如 packages/rrweb/test/replay/dialog.test.ts 及其 图片快照 目录)相互印证:回放端会依据该属性重建 dialog 的打开状态与层级。
3. 跨域 iframe 录制类型(PR #1035)
PR #1035 为跨域 iframe 录制支持补充了类型定义。核心是ICrossOriginIframeMirror接口(packages/types/src/index.ts#L297-L312),它定义了父页面与跨域 iframe 之间节点 ID 的映射能力:
getId(iframe, remoteId, ...):将 iframe 内的远端节点 ID 映射为主文档 ID;getRemoteId(iframe, parentId, ...):反向映射回 iframe 内的 ID;- 支持批量映射(
getIds/getRemoteIds)与按 iframe 重置(reset(iframe?))。
实现侧,packages/rrweb/src/record/cross-origin-iframe-mirror.ts 直接以import type { ICrossOriginIframeMirror } from '@rrweb/types'引用该接口;RecordPlugin.getMirror回调(packages/types/src/index.ts#L322-L326)也会向插件注入包含nodeMirror、crossOriginIframeMirror、crossOriginIframeStyleMirror三个镜像实例,其中后两个即为跨域 iframe 场景而设。测试见 packages/rrweb/test/record/cross-origin-iframes.test.ts。
4. mediaInteractionParam 新增 loop(PR #1432)
PR #1432 为媒体交互参数补充loop字段。当前类型定义(packages/types/src/index.ts#L649-L657):
export type mediaInteractionParam = { type: MediaInteractions; id: number; currentTime?: number; volume?: number; muted?: boolean; loop?: boolean; playbackRate?: number; };录制端在 packages/rrweb/src/record/observer.ts#L1036-L1045 中从媒体元素解构currentTime, volume, muted, playbackRate, loop并写入事件;对应地,快照侧mediaAttributes(packages/types/src/index.ts#L846-L865)也声明了rr_mediaLoop?: boolean等序列化属性。这使得回放时可以精确还原"循环播放"的媒体状态。
五、2.1.x 阶段:插件契约与资产事件
1. 网络插件类型进入共享包(2.1.0,PR #1689)
@rrweb/types2.1.0 的核心变更是"Add the network-plugin"。从此版本起,网络录制相关的类型正式纳入共享包,当前源码(packages/types/src/index.ts#L63-L123)包含一整套网络事件契约:
NetworkInitiatorType:资源发起类型联合,涵盖audio、beacon、fetch、iframe、img、script、xmlhttprequest等 21 种取值;NetworkRequest:基于PerformanceEntry派生并补充method、status、requestHeaders、requestBody、responseHeaders、responseBody、isInitial等字段的网络请求结构;NetworkRecordOptions:插件的配置类型,支持initiatorTypes(按发起类型过滤)、transformRequestFn(请求转换)、recordHeaders/recordBody(布尔或分请求/响应细粒度控制)、recordInitialRequests等;NetworkEvent = pluginEvent<NetworkData>:网络事件以插件事件(EventType.Plugin)的形式承载requests数组。
实际插件 packages/plugins/rrweb-plugin-network-record 与 packages/plugins/rrweb-plugin-network-replay 均直接依赖@rrweb/types(见各自 package.json),印证了"共享类型为插件生态服务"的设计意图。
2. Asset 事件类型别名(2.0.0 Patch,PR #1833)
PR #1833 为 2.0 事件契约补充公开的 Asset 事件类型别名。源码中对应:
export type assetEvent = { type: EventType.Asset; data: assetParam }; export type assetEventWithTime = assetEvent & { timestamp: number };assetParam(packages/types/src/index.ts#L691-L703)支持两种形态:成功时携带url与payload(如 Canvas 序列化参数或SerializedCssTextArg),失败时携带failed: { status?, message }。EventType.Asset在 EventType 枚举 中同样可见,用于承载全快照阶段资源(图片、样式等)的序列化结果或加载失败信息。
3. 紧凑样式变更修复(2.0.0 Patch,PR #1268)
PR #1268 修复并优化了紧凑(compact)样式变更:
- 修复样式更新中包含作用于简写属性(shorthand property)的
var()时的问题(issue #1246); - 进一步保证样式变更保持紧凑:若字符串形式更短则回退到字符串方法记录。
这一改动作用于styleSheetRuleParam/styleDeclarationParam相关的事件生成逻辑,目标是控制增量事件体积,属于不影响外部类型形状的内部优化。
六、版本发布节奏与依赖联动
从 changelog 可以还原@rrweb/types的发布节奏:
| 版本 | 类型 | 主要内容 |
|---|---|---|
| 2.0.0-alpha.5–7 | Patch | 仅跟随rrweb-snapshot变更 |
| 2.0.0-alpha.8 | Minor | 新增pointerType |
| 2.0.0-alpha.9–12 | Patch | 跟随rrweb-snapshot |
| 2.0.0-alpha.13 | Patch | mediaInteractionParam.loop、NodeNext 修复 |
| 2.0.0-alpha.14–16 | Patch | 跟随rrweb-snapshot |
| 2.0.0-alpha.17 | Minor | 顶层<dialog>支持 |
| 2.0.0-alpha.15/2.0.0 | Major | 产物分发体系重构、类型归属调整 |
| 2.0.0 正式版 | Major | 包拆分落地、Asset 别名、样式压缩修复、TS 4.9.5、UMD 目录 |
| 2.1.0 | Minor | 网络插件类型 |
| 2.1.1–2.1.5 | — | 版本同步(2.0.1 明确说明"仅版本号提升以与其他包保持同步") |
两个值得注意的规律:
- alpha 阶段大量 Patch 版本只是"跟随
rrweb-snapshot"——因为事件契约中的序列化节点类型(serializedNodeWithId等)定义在rrweb-snapshot中,@rrweb/types需要与之保持版本联动(如 2.0.0 条目中的 "Updated dependencies: rrweb-snapshot@2.0.0-alpha.4"); - 2.1.1–2.1.5 之间无实质变更,属于 monorepo 内的版本对齐(
changesets批量发布机制所致),网络插件相关类型在 2.1.0 已就位,因此当前各包统一依赖^2.1.5是安全的。
七、实践指南:如何消费 @rrweb/types
安装与导入
@rrweb/types通常是作为rrweb等包的传递依赖被安装的,一般无需显式安装;若插件开发或自定义事件处理需要直接使用其类型,可显式安装:
npm install --save-dev @rrweb/types # 或 yarn add --dev @rrweb/types导入方式(ESM / 类型导入):
import type { eventWithTime, IncrementalSource, EventType } from '@rrweb/types'; import { EventType, IncrementalSource } from '@rrweb/types'; // 枚举可直接导入注意:由于exports字段的限制,不应使用@rrweb/types/dist/...之类的深路径导入;且event类型在源码中已被标记为@deprecated(packages/types/src/index.ts#L241-L245),它是eventWithoutTime的同义词,仅供内部使用,新代码应直接使用eventWithoutTime或eventWithTime。
典型使用场景
- 自定义事件:使用
customEvent<T>类型约束event.data.tag与payload; - 插件开发:基于
RecordPlugin<TOptions>(packages/types/src/index.ts#L314-L328)编写录制插件,实现observer、eventProcessor、getMirror等钩子,类型契约保证插件与主包解耦; - 事件处理管道:以
eventWithTime为输入,编写存储、加密、压缩(如 @rrweb/packer)或实时转发逻辑; - 跨包类型复用:在
rrweb、rrweb-snapshot、回放包之间传递serializedNodeWithId等节点类型时,统一从@rrweb/types引入。
升级到 2.x 的检查清单
- 检查是否有直接引用
rrweb/typings/...、rrdom/es等旧路径的代码,按新版exports映射调整; <script>直引场景改用.umd.cjs文件,注意dist下.js均为 ESM;PlayerMachineState、SpeedMachineState等回放状态类型改从@rrweb/replay导入;"moduleResolution": "NodeNext"项目请升级到 2.0.0 及以上(已修复类型解析);- 消费增量事件时,对新增的可选字段(
pointerType、loop等)保持空值兼容,以支持新旧录制数据并存。
结语
@rrweb/types虽是一个"只有类型、没有逻辑"的包,却是 rrweb 2.0 生态正常运转的契约基石:它承载了从EventType/IncrementalSource到插件接口的完整事件模型,其版本演进史(packages/types/CHANGELOG.md)浓缩了 rrweb 2.0 在分发体系、指针事件、dialog、跨域 iframe、媒体状态与插件生态上的全部关键变化。理解这个包,就等于拿到了读懂 rrweb 事件流与进行二次开发的类型地图。
- 前端
- 可观测性
- 开发工具
【免费下载链接】rrweb
record and replay the web
相关推荐
StaffML Vault 共享类型包(@staffml/vault-types)技术指南:Schema v1.0 类型契约与跨端集成实践
StaffML Vault 共享类型包(@staffml/vault types)技术指南:Schema v1.0 类型契约与跨端集成实践 导读 @staffm
教育教程人工智能机器学习@redux-saga/types 类型系统深度解析:共享类型包的演进与 TypeScript 最佳实践
@redux saga/types 类型系统深度解析:共享类型包的演进与 TypeScript 最佳实践 本文以 @redux saga/types 包的变更日
前端从0到1开发Android聊天应用:基于Chateau框架的完整案例
从0到1开发Android聊天应用:基于Chateau框架的完整案例 Chateau是一个功能强大的Android聊天框架,能够帮助开发者快速在任何Androi
前端可观测性开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考