perf: Avoid an extra function call and object clone during event emission (#1441)

performance: remove a nested function call and an object clone during event emission

 - rename `event` to `eventWithoutTime`, but maintain backwards compatibility
 - `eventWithTime` (with time) could be renamed to `event` in a future version

This is an extension of PR #1339 authored by: mydea <mydea@users.noreply.github.com>
This commit is contained in:
Eoghan Murray
2024-04-19 12:50:06 +01:00
committed by GitHub
parent c0f83afab8
commit ae6908dcdc
5 changed files with 151 additions and 177 deletions

View File

@@ -0,0 +1,5 @@
---
'rrweb': patch
---
perf: Avoid an extra function call and object clone during event emission

View File

@@ -3,7 +3,11 @@ import { genId, NodeType } from 'rrweb-snapshot';
import type { CrossOriginIframeMessageEvent } from '../types'; import type { CrossOriginIframeMessageEvent } from '../types';
import CrossOriginIframeMirror from './cross-origin-iframe-mirror'; import CrossOriginIframeMirror from './cross-origin-iframe-mirror';
import { EventType, IncrementalSource } from '@rrweb/types'; import { EventType, IncrementalSource } from '@rrweb/types';
import type { eventWithTime, mutationCallBack } from '@rrweb/types'; import type {
eventWithTime,
eventWithoutTime,
mutationCallBack,
} from '@rrweb/types';
import type { StylesheetManager } from './stylesheet-manager'; import type { StylesheetManager } from './stylesheet-manager';
export class IframeManager { export class IframeManager {
@@ -16,7 +20,7 @@ export class IframeManager {
new WeakMap(); new WeakMap();
private mirror: Mirror; private mirror: Mirror;
private mutationCb: mutationCallBack; private mutationCb: mutationCallBack;
private wrappedEmit: (e: eventWithTime, isCheckout?: boolean) => void; private wrappedEmit: (e: eventWithoutTime, isCheckout?: boolean) => void;
private loadListener?: (iframeEl: HTMLIFrameElement) => unknown; private loadListener?: (iframeEl: HTMLIFrameElement) => unknown;
private stylesheetManager: StylesheetManager; private stylesheetManager: StylesheetManager;
private recordCrossOriginIframes: boolean; private recordCrossOriginIframes: boolean;
@@ -26,7 +30,7 @@ export class IframeManager {
mutationCb: mutationCallBack; mutationCb: mutationCallBack;
stylesheetManager: StylesheetManager; stylesheetManager: StylesheetManager;
recordCrossOriginIframes: boolean; recordCrossOriginIframes: boolean;
wrappedEmit: (e: eventWithTime, isCheckout?: boolean) => void; wrappedEmit: (e: eventWithoutTime, isCheckout?: boolean) => void;
}) { }) {
this.mutationCb = options.mutationCb; this.mutationCb = options.mutationCb;
this.wrappedEmit = options.wrappedEmit; this.wrappedEmit = options.wrappedEmit;

View File

@@ -19,7 +19,7 @@ import {
import type { recordOptions } from '../types'; import type { recordOptions } from '../types';
import { import {
EventType, EventType,
event, eventWithoutTime,
eventWithTime, eventWithTime,
IncrementalSource, IncrementalSource,
listenerHandler, listenerHandler,
@@ -40,14 +40,7 @@ import {
unregisterErrorHandler, unregisterErrorHandler,
} from './error-handler'; } from './error-handler';
function wrapEvent(e: event): eventWithTime { let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void;
return {
...e,
timestamp: nowTimestamp(),
};
}
let wrappedEmit!: (e: eventWithTime, isCheckout?: boolean) => void;
let takeFullSnapshot!: (isCheckout?: boolean) => void; let takeFullSnapshot!: (isCheckout?: boolean) => void;
let canvasManager!: CanvasManager; let canvasManager!: CanvasManager;
@@ -187,7 +180,9 @@ function record<T = eventWithTime>(
} }
return e as unknown as T; return e as unknown as T;
}; };
wrappedEmit = (e: eventWithTime, isCheckout?: boolean) => { wrappedEmit = (r: eventWithoutTime, isCheckout?: boolean) => {
const e = r as eventWithTime;
e.timestamp = nowTimestamp();
if ( if (
mutationBuffers[0]?.isFrozen() && mutationBuffers[0]?.isFrozen() &&
e.type !== EventType.FullSnapshot && e.type !== EventType.FullSnapshot &&
@@ -238,47 +233,39 @@ function record<T = eventWithTime>(
}; };
const wrappedMutationEmit = (m: mutationCallbackParam) => { const wrappedMutationEmit = (m: mutationCallbackParam) => {
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.Mutation,
source: IncrementalSource.Mutation, ...m,
...m, },
}, });
}),
);
}; };
const wrappedScrollEmit: scrollCallback = (p) => const wrappedScrollEmit: scrollCallback = (p) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.Scroll,
source: IncrementalSource.Scroll, ...p,
...p, },
}, });
}),
);
const wrappedCanvasMutationEmit = (p: canvasMutationParam) => const wrappedCanvasMutationEmit = (p: canvasMutationParam) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.CanvasMutation,
source: IncrementalSource.CanvasMutation, ...p,
...p, },
}, });
}),
);
const wrappedAdoptedStyleSheetEmit = (a: adoptedStyleSheetParam) => const wrappedAdoptedStyleSheetEmit = (a: adoptedStyleSheetParam) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.AdoptedStyleSheet,
source: IncrementalSource.AdoptedStyleSheet, ...a,
...a, },
}, });
}),
);
const stylesheetManager = new StylesheetManager({ const stylesheetManager = new StylesheetManager({
mutationCb: wrappedMutationEmit, mutationCb: wrappedMutationEmit,
@@ -350,14 +337,14 @@ function record<T = eventWithTime>(
return; return;
} }
wrappedEmit( wrappedEmit(
wrapEvent({ {
type: EventType.Meta, type: EventType.Meta,
data: { data: {
href: window.location.href, href: window.location.href,
width: getWindowWidth(), width: getWindowWidth(),
height: getWindowHeight(), height: getWindowHeight(),
}, },
}), },
isCheckout, isCheckout,
); );
@@ -406,13 +393,13 @@ function record<T = eventWithTime>(
} }
wrappedEmit( wrappedEmit(
wrapEvent({ {
type: EventType.FullSnapshot, type: EventType.FullSnapshot,
data: { data: {
node, node,
initialOffset: getWindowScroll(window), initialOffset: getWindowScroll(window),
}, },
}), },
isCheckout, isCheckout,
); );
mutationBuffers.forEach((buf) => buf.unlock()); // generate & emit any mutations that happened during snapshotting, as can now apply against the newly built mirror mutationBuffers.forEach((buf) => buf.unlock()); // generate & emit any mutations that happened during snapshotting, as can now apply against the newly built mirror
@@ -433,108 +420,88 @@ function record<T = eventWithTime>(
{ {
mutationCb: wrappedMutationEmit, mutationCb: wrappedMutationEmit,
mousemoveCb: (positions, source) => mousemoveCb: (positions, source) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source,
source, positions,
positions, },
}, }),
}),
),
mouseInteractionCb: (d) => mouseInteractionCb: (d) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.MouseInteraction,
source: IncrementalSource.MouseInteraction, ...d,
...d, },
}, }),
}),
),
scrollCb: wrappedScrollEmit, scrollCb: wrappedScrollEmit,
viewportResizeCb: (d) => viewportResizeCb: (d) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.ViewportResize,
source: IncrementalSource.ViewportResize, ...d,
...d, },
}, }),
}),
),
inputCb: (v) => inputCb: (v) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.Input,
source: IncrementalSource.Input, ...v,
...v, },
}, }),
}),
),
mediaInteractionCb: (p) => mediaInteractionCb: (p) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.MediaInteraction,
source: IncrementalSource.MediaInteraction, ...p,
...p, },
}, }),
}),
),
styleSheetRuleCb: (r) => styleSheetRuleCb: (r) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.StyleSheetRule,
source: IncrementalSource.StyleSheetRule, ...r,
...r, },
}, }),
}),
),
styleDeclarationCb: (r) => styleDeclarationCb: (r) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.StyleDeclaration,
source: IncrementalSource.StyleDeclaration, ...r,
...r, },
}, }),
}),
),
canvasMutationCb: wrappedCanvasMutationEmit, canvasMutationCb: wrappedCanvasMutationEmit,
fontCb: (p) => fontCb: (p) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.Font,
source: IncrementalSource.Font, ...p,
...p, },
}, }),
}),
),
selectionCb: (p) => { selectionCb: (p) => {
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.Selection,
source: IncrementalSource.Selection, ...p,
...p, },
}, });
}),
);
}, },
customElementCb: (c) => { customElementCb: (c) => {
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.IncrementalSnapshot,
type: EventType.IncrementalSnapshot, data: {
data: { source: IncrementalSource.CustomElement,
source: IncrementalSource.CustomElement, ...c,
...c, },
}, });
}),
);
}, },
blockClass, blockClass,
ignoreClass, ignoreClass,
@@ -570,15 +537,13 @@ function record<T = eventWithTime>(
observer: p.observer!, observer: p.observer!,
options: p.options, options: p.options,
callback: (payload: object) => callback: (payload: object) =>
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.Plugin,
type: EventType.Plugin, data: {
data: { plugin: p.name,
plugin: p.name, payload,
payload, },
}, }),
}),
),
})) || [], })) || [],
}, },
hooks, hooks,
@@ -607,12 +572,10 @@ function record<T = eventWithTime>(
} else { } else {
handlers.push( handlers.push(
on('DOMContentLoaded', () => { on('DOMContentLoaded', () => {
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.DomContentLoaded,
type: EventType.DomContentLoaded, data: {},
data: {}, });
}),
);
if (recordAfter === 'DOMContentLoaded') init(); if (recordAfter === 'DOMContentLoaded') init();
}), }),
); );
@@ -620,12 +583,10 @@ function record<T = eventWithTime>(
on( on(
'load', 'load',
() => { () => {
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.Load,
type: EventType.Load, data: {},
data: {}, });
}),
);
if (recordAfter === 'load') init(); if (recordAfter === 'load') init();
}, },
window, window,
@@ -648,15 +609,13 @@ record.addCustomEvent = <T>(tag: string, payload: T) => {
if (!recording) { if (!recording) {
throw new Error('please add custom event after start recording'); throw new Error('please add custom event after start recording');
} }
wrappedEmit( wrappedEmit({
wrapEvent({ type: EventType.Custom,
type: EventType.Custom, data: {
data: { tag,
tag, payload,
payload, },
}, });
}),
);
}; };
record.freezePage = () => { record.freezePage = () => {

View File

@@ -3,10 +3,10 @@ import {
EventType, EventType,
IncrementalSource, IncrementalSource,
eventWithTime, eventWithTime,
eventWithoutTime,
MouseInteractions, MouseInteractions,
Optional, Optional,
mouseInteractionData, mouseInteractionData,
event,
pluginEvent, pluginEvent,
} from '@rrweb/types'; } from '@rrweb/types';
import type { recordOptions } from '../src/types'; import type { recordOptions } from '../src/types';
@@ -228,7 +228,7 @@ function stringifySnapshots(snapshots: eventWithTime[]): string {
} }
} }
delete (s as Optional<eventWithTime, 'timestamp'>).timestamp; delete (s as Optional<eventWithTime, 'timestamp'>).timestamp;
return s as event; return s as eventWithoutTime;
}), }),
null, null,
2, 2,

View File

@@ -163,7 +163,7 @@ export type incrementalData =
| adoptedStyleSheetData | adoptedStyleSheetData
| customElementData; | customElementData;
export type event = export type eventWithoutTime =
| domContentLoadedEvent | domContentLoadedEvent
| loadedEvent | loadedEvent
| fullSnapshotEvent | fullSnapshotEvent
@@ -172,7 +172,13 @@ export type event =
| customEvent | customEvent
| pluginEvent; | pluginEvent;
export type eventWithTime = event & { /**
* @deprecated intended for internal use
* a synonym for eventWithoutTime
*/
export type event = eventWithoutTime;
export type eventWithTime = eventWithoutTime & {
timestamp: number; timestamp: number;
delay?: number; delay?: number;
}; };