From 09f30d2a81f71f82d1cb96140cd0e9469f07c870 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Wed, 1 Apr 2026 12:00:00 +0800 Subject: [PATCH] return stopper function as the result of record --- src/record/index.ts | 177 ++++++++++++++++++++++------------------- src/record/observer.ts | 16 ++-- src/utils.ts | 5 +- 3 files changed, 105 insertions(+), 93 deletions(-) diff --git a/src/record/index.ts b/src/record/index.ts index b886607f..c2ec8e5a 100644 --- a/src/record/index.ts +++ b/src/record/index.ts @@ -7,6 +7,7 @@ import { eventWithTime, recordOptions, IncrementalSource, + listenerHandler, } from '../types'; function wrapEvent(e: event): eventWithTime { @@ -16,21 +17,24 @@ function wrapEvent(e: event): eventWithTime { }; } -function record(options: recordOptions = {}) { +function record(options: recordOptions = {}): listenerHandler | undefined { const { emit } = options; // runtime checks for user options if (!emit) { throw new Error('emit function is required'); } try { - on('DOMContentLoaded', () => { - emit( - wrapEvent({ - type: EventType.DomContentLoaded, - data: {}, - }), - ); - }); + const handlers: listenerHandler[] = []; + handlers.push( + on('DOMContentLoaded', () => { + emit( + wrapEvent({ + type: EventType.DomContentLoaded, + data: {}, + }), + ); + }), + ); const init = () => { emit( wrapEvent({ @@ -59,68 +63,70 @@ function record(options: recordOptions = {}) { }, }), ); - initObservers({ - mutationCb: m => - emit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.Mutation, - ...m, - }, - }), - ), - mousemoveCb: positions => - emit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.MouseMove, - positions, - }, - }), - ), - mouseInteractionCb: d => - emit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.MouseInteraction, - ...d, - }, - }), - ), - scrollCb: p => - emit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.Scroll, - ...p, - }, - }), - ), - viewportResizeCb: d => - emit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.ViewportResize, - ...d, - }, - }), - ), - inputCb: v => - emit( - wrapEvent({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.Input, - ...v, - }, - }), - ), - }); + handlers.push( + initObservers({ + mutationCb: m => + emit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + ...m, + }, + }), + ), + mousemoveCb: positions => + emit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseMove, + positions, + }, + }), + ), + mouseInteractionCb: d => + emit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseInteraction, + ...d, + }, + }), + ), + scrollCb: p => + emit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Scroll, + ...p, + }, + }), + ), + viewportResizeCb: d => + emit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.ViewportResize, + ...d, + }, + }), + ), + inputCb: v => + emit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + ...v, + }, + }), + ), + }), + ); }; if ( document.readyState === 'interactive' || @@ -128,20 +134,25 @@ function record(options: recordOptions = {}) { ) { init(); } else { - on( - 'load', - () => { - emit( - wrapEvent({ - type: EventType.Load, - data: {}, - }), - ); - init(); - }, - window, + handlers.push( + on( + 'load', + () => { + emit( + wrapEvent({ + type: EventType.Load, + data: {}, + }), + ); + init(); + }, + window, + ), ); } + return () => { + handlers.forEach(h => h()); + }; } catch (error) { // TODO: handle internal error console.warn(error); diff --git a/src/record/observer.ts b/src/record/observer.ts index bdd9b266..778be3aa 100644 --- a/src/record/observer.ts +++ b/src/record/observer.ts @@ -365,7 +365,7 @@ function initInputObserver(cb: inputCallback): listenerHandler { }; } -export default function initObservers(o: observerParam) { +export default function initObservers(o: observerParam): listenerHandler { const mutationObserver = initMutationObserver(o.mutationCb); const mousemoveHandler = initMousemoveObserver(o.mousemoveCb); const mouseInteractionHandler = initMouseInteractionObserver( @@ -374,12 +374,12 @@ export default function initObservers(o: observerParam) { const scrollHandler = initScrollObserver(o.scrollCb); const viewportResizeHandler = initViewportResizeObserver(o.viewportResizeCb); const inputHandler = initInputObserver(o.inputCb); - return { - mutationObserver, - mousemoveHandler, - mouseInteractionHandler, - scrollHandler, - viewportResizeHandler, - inputHandler, + return () => { + mutationObserver.disconnect(); + mousemoveHandler(); + mouseInteractionHandler(); + scrollHandler(); + viewportResizeHandler(); + inputHandler(); }; } diff --git a/src/utils.ts b/src/utils.ts index afaa1d6a..4d588cfd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,8 +11,9 @@ export function on( fn: EventListenerOrEventListenerObject, target: Document | Window = document, ): listenerHandler { - target.addEventListener(type, fn, { capture: true, passive: true }); - return () => target.removeEventListener(type, fn); + const options = { capture: true, passive: true }; + target.addEventListener(type, fn, options); + return () => target.removeEventListener(type, fn, options); } export const mirror: Mirror = {