
前端屏幕录制【免费下载链接】rrwebrecord and replay the web项目地址https://gitcode.com/gh_mirrors/rr/rrweb点击查看免费下载导读rrweb/utils是 rrweb 开源仓库record and replay the web中所有包共享的工具函数集集中解决两类核心问题在页面被第三方库Angular、LWC、wujie 等猴子补丁污染原生 DOM 原型之后如何仍然稳定可靠地遍历、读取真实的 DOM 结构以及如何安全地挂钩原生 API 进行录制同时保证可以随时还原。阅读本文后你将理解 rrweb 录制链路中防污染 DOM 访问的设计动机与实现原理掌握patch工具的还原式补丁模式并能独立阅读 packages/utils/src/index.ts 及其在下游record、rrweb-snapshot、插件中的实际用法。一、包的定位与仓库结构rrweb/utils在 monorepo 中承担公共底座角色仓库根目录的包索引将其描述为 Shared utility functions used across rrweb packagespackages/README.md。包的官方 READMEpackages/utils/README.md虽仅给出简短定位但源码与下游消费足以说明其价值它是 rrweb 录制管线的地基层。整个包的结构非常精简唯一源码入口 packages/utils/src/index.ts聚合全部导出构建脚本定义在 packages/utils/package.jsonbuild为tsc -noEmit vite builddev为vite build --watchVite 库构建配置复用仓库根目录的 vite.config.default.ts以rrwebUtils作为 UMD 全局名见 packages/utils/vite.config.js包的exports同时提供 ESM./dist/utils.js与 CJS/UMD./dist/utils.umd.cjs入口并发布dist与umd目录packages/utils/package.json。该包没有运行时依赖dependencies: {}是一个零依赖的纯工具包这也让它在各包之间共享时不会引入重复依赖体积。二、为何需要未污染原型工具录制器与原型的对抗要理解rrweb/utils里最核心的一批函数getUntaintedPrototype、getUntaintedAccessor、getUntaintedMethod及其封装必须先理解 rrweb 录制器面临的环境rrweb 的录制器通过MutationObserver观察 DOM 变更通过遍历childNodes、parentNode等访问器重建页面快照然而现实页面中Angularzone.js、LWC、wujie 等框架与第三方库会直接改写这些原生原型上的方法monkey patch。例如源码注释明确提到 wujie monkeypatches ownerDocument见 packages/utils/CHANGELOG.md一旦Node.prototype.ownerDocument等访问器被改写录制器遍历 DOM 时可能读到被加工过的、不真实的节点关系导致快照错乱更隐蔽的问题是性能源码注释指出 Angular 补丁后的MutationObserver能通过isNativeFunction检查但 Angular 的变更检测不喜欢共享同一个 mutation observer直接复用会导致严重性能问题packages/utils/src/index.ts。因此rrweb/utils提供了回到未被污染的浏览器原生实现的能力。这是 rrweb 录制正确性与性能的双重保证。三、防污染原型核心实现getUntaintedPrototypegetUntaintedPrototypepackages/utils/src/index.ts负责获取某一类对象Node、ShadowRoot、MutationObserver、Element未被污染的原始 prototype核心逻辑分三层1. 访问器与方法的原生性探测。包内定义了testableAccessors与testableMethods两张检查清单packages/utils/src/index.tsNode的访问器childNodes、parentNode、parentElement、textContent、ownerDocument、firstChild、lastChild、nextSibling、previousSibling方法contains、getRootNodeShadowRoot的访问器host、styleSheets方法getSelectionElement的访问器shadowRoot、querySelector、querySelectorAllMutationObserver仅方法constructor。判定方式是把访问器的 getter 与方法的toString()结果与[native code]比对如果全部命中说明原型未被改写可直接使用。2. Angular Zone 探测。isAngularZonePresentpackages/utils/src/index.ts通过检查globalThis.Zone是否存在来推断 Angular zone.js 是否在场。源码注释提醒即使原型本身通过原生性检查只要 Angular 在场也不应直接复用其MutationObserver因为 Angular 的变更检测与录制器共用 observer 会引发性能问题。该检测是足够好的代理good-enough proxy注释同时给出定制方案可以在全局对象上配置 zone.js 使用不同名称并让 rrweb 在 Zone 之外手动运行。3. iframe 兜底获取原生原型。若探测发现原型被污染或 Angular 在场则创建一个隐藏 iframe从iframe.contentWindow中取回该浏览器原生构造函数及其 prototypepackages/utils/src/index.ts。这一段有几个值得注意的工程细节iframe 添加rr-blockclass 并设置__rrwebUntaintedMutationObserver属性避免后续快照把该 iframe 序列化进去WebKit/Safari 特殊处理WebKit 在 iframe 脱离 DOM 时会销毁其 ScriptExecutionContextMutationObserver.deliver()会因回调的 execution context 为 null 而静默丢弃回调注释引用了 webkit.org/b/179224。因此 Safari 下 iframe 保持挂载并暴露清理函数untaintedBaseIframeCleanup[key]供调用方在录制结束时移除其余浏览器则立即iframe.remove()结果会缓存进untaintedBasePrototype避免重复创建 iframe。四、访问器与方法封装getUntaintedAccessor / getUntaintedMethod拿到未污染原型后还需把它应用到具体节点实例上。getUntaintedAccessorpackages/utils/src/index.ts从未污染原型上取出属性描述符的 gettercall到传入的节点实例上结果按key accessor缓存getUntaintedMethodpackages/utils/src/index.ts则以key.method为缓存键将未污染方法bind到实例上返回。基于这两者包对外导出一整套安全 DOM 读取 API并在模块末尾以export default聚合导出packages/utils/src/index.ts导出对应原生 API类型ownerDocument(n)Node.prototype.ownerDocument访问器childNodes(n)Node.prototype.childNodes访问器parentNode(n)Node.prototype.parentNode访问器parentElement(n)Node.prototype.parentElement访问器textContent(n)Node.prototype.textContent访问器firstChild(n)/lastChild(n)Node.prototype首个/末个子节点访问器nextSibling(n)/previousSibling(n)Node.prototype相邻兄弟节点访问器contains(n, other)Node.prototype.contains方法getRootNode(n)Node.prototype.getRootNode方法host(n)ShadowRoot.prototype.host访问器styleSheets(n)ShadowRoot.prototype.styleSheets访问器shadowRoot(n)Element.prototype.shadowRoot访问器querySelector(n, s)/querySelectorAll(n, s)Element.prototype查询方法访问器mutationObserverCtor()MutationObserver构造器含 Safari 清理函数方法nowTimestamp防篡改的当前时间戳工具patch还原式猴子补丁工具注意host(n)、shadowRoot(n)对传入对象先做能力检查host in n、shadowRoot in n不存在时返回null避免对普通节点误报。五、防篡改时间戳nowTimestamp录制事件需要精确的时间戳但第三方库同样可能改写Date.now。nowTimestamp的实现做了防御性校验packages/utils/src/index.tslet nowTimestamp Date.now; if (!(/[1-9][0-9]{12}/.test(Date.now().toString()))) { nowTimestamp () new Date().getTime(); }原理正常的毫秒级时间戳是 13 位数字以1开头正则[1-9][0-9]{12}用于识别看起来是合法时间戳的输出。一旦Date.now已被改写导致输出不符合该形态就回退到new Date().getTime()保证录制时间线始终可靠。该函数被 packages/rrweb/src/record/index.ts 直接导入用于给每个事件打时间戳packages/rrweb/src/record/index.ts也被 packages/rrweb/src/record/observer.ts 用于鼠标移动事件的时间基线计算packages/rrweb/src/record/observer.ts。在 2.0.0 版本中nowTimestamp被从rrweb/utils迁入rrweb/utils目的是让新的浏览器客户端包browser-client不必再通过rrweb/utils间接导入见 packages/utils/CHANGELOG.md目前 packages/browser-client/src/index.ts 正是以import { nowTimestamp } from rrweb/utils的方式直接消费它。六、还原式补丁工具patchpatch(source, name, replacement)packages/utils/src/index.ts提供可还原的猴子补丁能力设计上借鉴了 Sentry JavaScript SDK 的同类实现源码注释标明出处。其行为契约如下若name不在source上直接返回 no-op 还原函数否则取original source[name]调用replacement(original)得到包装函数wrapped若wrapped是函数为其挂上不可枚举的__rrweb_original__属性指向原始实现Object.defineProperties以便外部识别用wrapped替换source[name]并返回还原函数() { source[name] original; }整体包在 try/catch 中当多个补丁同时作用在XMLHttpRequest这类全局对象上时可能抛错注释链接了 Sentry 的 issue #2043此时也返回 no-op 还原函数保证调用方安全。patch的消费方遍布录制管线packages/rrweb/src/record/observer.ts 用它包装doc.fonts.add在字体加入 FontFaceSet 时回调解码后的字体信息并注册还原处理器packages/rrweb/src/record/observer.ts 用它包装customElements.define捕获自定义元素注册事件packages/rrweb/src/record/observers/canvas/2d.ts、packages/rrweb/src/record/observers/canvas/webgl.ts 用它挂钩 Canvas 2D/WebGL 绘制调用网络录制插件 packages/plugins/rrweb-plugin-network-record/src/index.ts 用它包装XMLHttpRequest.prototype.open与win.fetchpackages/plugins/rrweb-plugin-network-record/src/index.ts在保持原始行为的同时采集请求/响应信息控制台录制插件 packages/plugins/rrweb-plugin-console-record/src/index.ts 用它替换console.log等各级方法并借助返回的还原函数在录制停止时恢复原 console。每个消费方拿到返回的还原函数后都会在录制结束的handlers列表中统一执行实现用完即还原、不留痕迹。七、MutationObserver 的纯净构造器mutationObserverCtormutationObserverCtor()packages/utils/src/index.ts返回[未污染的 MutationObserver 构造器, iframe 清理函数]。第二项在 Safari 下是真实的 iframe 移除函数配合 WebKit 上下文存活策略其余浏览器下是 no-op。在录制器中packages/rrweb/src/record/observer.ts 的initMutationObserver正是用它创建观察器const [ObserverCtor, iframeCleanup] mutationObserverCtor(); const observer new (ObserverCtor as new (callback: MutationCallback) MutationObserver)( callbackWrapper(mutationBuffer.processMutations.bind(mutationBuffer)), ); observer.observe(rootEl, { attributes: true, attributeOldValue: true, characterData: true, characterDataOldValue: true, childList: true, subtree: true, });这样即使页面被 Angular 等框架补丁过MutationObserver录制器仍使用原生实现既规避了共用 observer 导致的性能问题也避免被框架的变更检测行为干扰。CHANGELOG 中 2.0.1 版本的修复项即为此而生Fix broken MutationObserver in Webkit/Safari when a webpage has been monkey patched by a 3rd party librarypackages/utils/CHANGELOG.md。八、安全 DOM API 在录制与快照链路中的实际落地rrweb/utils的默认导出dom对象被录制器与快照器大量使用以下实例均可在源码中直接验证1. 快照序列化rrweb-snapshot。packages/rrweb-snapshot/src/snapshot.ts 导入dom序列化时通过dom.childNodes(el)判断节点是否有子节点packages/rrweb-snapshot/src/snapshot.ts、dom.textContent(n)读取文本内容packages/rrweb-snapshot/src/snapshot.ts、dom.parentNode(node)实现按祖先类名匹配packages/rrweb-snapshot/src/snapshot.ts并用dom.shadowRoot(n)判断元素是否挂载了 shadow root 后递归序列化其内部节点packages/rrweb-snapshot/src/snapshot.ts。2. Mutation 缓冲与增量快照rrweb record。packages/rrweb/src/record/mutation.ts 的MutationBuffer处理 DOM 变更时全部通过dom.parentNode(n)、dom.previousSibling(n)、dom.nextSibling(n)、dom.lastChild(parentNode)、dom.childNodes(n)、dom.textContent(m.target)、dom.host(...)、dom.shadowRoot(n)等读取真实节点关系packages/rrweb/src/record/mutation.ts确保增删改事件里的父子、兄弟位置在受污染环境下依然准确。3. Shadow DOM 管理。packages/rrweb/src/record/shadow-dom-manager.ts 导入dom与patch通过dom.host(shadowRoot)记录 shadow root 的宿主节点 IDpackages/rrweb/src/record/shadow-dom-manager.ts、dom.shadowRoot(this)获取元素挂载的 shadow rootpackages/rrweb/src/record/shadow-dom-manager.ts。4. 输入事件定位。packages/rrweb/src/record/observer.ts 中当option元素的值变化时用dom.parentElement(target)将变更归因到其父级select保证增量事件归属正确。这些用例共同说明防污染 API 不是孤立的工具而是贯穿全量快照 → 增量变更 → Shadow DOM → 输入/交互事件整条录制链路的底层基础设施。九、包的演进脉络来自 CHANGELOG从 packages/utils/CHANGELOG.md 可以梳理出该包的形成过程它直接反映了 rrweb 工程化的两次关键决策2.0.0将patch函数迁入rrweb/utils以改善打包体积#1631packages/utils/CHANGELOG.md提供umd输出目录使 UMD 文件可以带.js扩展名而不破坏 package.json 中dist 下所有 .js 均为模块的约定#1704packages/utils/CHANGELOG.md新增反向补丁内建方法以支持 LWC 与 Angular 等框架#1509packages/utils/CHANGELOG.md修复 wujie 对ownerDocument的补丁#1763packages/utils/CHANGELOG.md将nowTimestamp移入本包以服务新 browser-client 包#1840packages/utils/CHANGELOG.md2.0.1修复 WebKit/Safari 下MutationObserver被第三方库补丁后失效的问题#1854packages/utils/CHANGELOG.md。从依赖关系看rrweb/utils目前被 packages/browser-client/package.json、packages/plugins/rrweb-plugin-console-record/package.json、packages/plugins/rrweb-plugin-network-record/package.json 等显式声明为运行时依赖是录制生态共享的公共底座。十、阅读与调试建议源码入口packages/utils/src/index.ts 共约 350 行按类型定义 → 防污染原型 → 安全访问器/方法 → 时间戳 → patch的顺序阅读即可完整把握包的结构构建产物本地执行yarn workspace rrweb/utils build对应 package.json 中的build脚本可产出 ESM 与 UMD 产物UMD 全局名为rrwebUtils验证行为包的消费测试分散在下游各包例如 packages/browser-client/test/ssr-import.test.ts 对rrweb/utils做了 mock若要观察防污染逻辑在真实页面下的效果可在安装了 Angular/wujie 等框架的页面上录制对比开启前后的 DOM 变更事件输出阅读顺序建议先读本包源码再读 packages/rrweb/src/record/observer.ts 中的initMutationObserver与initInputObserver随后进入 packages/rrweb/src/record/mutation.ts 和 packages/rrweb-snapshot/src/snapshot.ts可以形成从工具到录制链路的完整认知。小结rrweb/utils以极小的代码量解决了 rrweb 录制器最棘手的现实问题第三方库对浏览器原生 API 的改写。它通过原生性探测 iframe 兜底 缓存获取未污染的 DOM 访问能力通过patch提供可还原的挂钩机制通过nowTimestamp保证时间线可靠并被record、rrweb-snapshot、browser-client及各插件广泛消费。理解这一层基础设施是深入阅读 rrweb 录制源码的前提。赞分享前端屏幕录制【免费下载链接】rrwebrecord and replay the web项目地址https://gitcode.com/gh_mirrors/rr/rrweb点击查看免费下载相关推荐rrweb 系列包之 rrweb/utils为录制内核提供未污染 DOM API的公共基础设施rrweb 系列包之 rrweb/utils为录制内核提供未污染 DOM API的公共基础设施 本篇文章以 packages/utils/CHANGEL前端屏幕录制rrweb 共享类型包 rrweb/types 完全指南事件模型、增量数据源与包间数据 APIrrweb 共享类型包 rrweb/types 完全指南事件模型、增量数据源与包间数据 API 导读 rrweb/types 是 rrweb 会话录制r前端屏幕录制rrweb-snapshot 深度解析rrweb 的 DOM 序列化与重建引擎rrweb snapshot 深度解析rrweb 的 DOM 序列化与重建引擎 rrweb snapshot 是 rrweb 会话录制体系中负责DOM 快照前端屏幕录制上一篇OneUptime Terraform Provider 自托管接入指南URL 指向、版本选择、离线镜像与 TLS 配置下一篇PKHeX插件全攻略从手动调整到智能合规的宝可梦管理革命创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考