feat: add a destroy function to destroy the whole player (#953)
* feat: add a destroy function to destroy the whole player * doc: update guidance document * Update packages/rrweb/src/replay/index.ts Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com> Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
This commit is contained in:
4
guide.md
4
guide.md
@@ -280,6 +280,9 @@ replayer.pause();
|
|||||||
|
|
||||||
// pause at the fifth seconds
|
// pause at the fifth seconds
|
||||||
replayer.pause(5000);
|
replayer.pause(5000);
|
||||||
|
|
||||||
|
// destroy the replayer (hint: this operation is irreversible)
|
||||||
|
replayer.destroy();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Options
|
#### Options
|
||||||
@@ -385,6 +388,7 @@ The event list:
|
|||||||
| mouse-interaction | mouse interaction has been replayed | { type, target } |
|
| mouse-interaction | mouse interaction has been replayed | { type, target } |
|
||||||
| event-cast | event has been replayed | event |
|
| event-cast | event has been replayed | event |
|
||||||
| custom-event | custom event has been replayed | event |
|
| custom-event | custom event has been replayed | event |
|
||||||
|
| destroy | destroyed the replayer | - |
|
||||||
|
|
||||||
The rrweb-replayer also re-expose the event listener via a `component.addEventListener` API.
|
The rrweb-replayer also re-expose the event listener via a `component.addEventListener` API.
|
||||||
|
|
||||||
|
|||||||
@@ -276,6 +276,9 @@ replayer.pause();
|
|||||||
|
|
||||||
// 暂停至第 5 秒处
|
// 暂停至第 5 秒处
|
||||||
replayer.pause(5000);
|
replayer.pause(5000);
|
||||||
|
|
||||||
|
// 销毁播放器 (提示: 这个操作不可逆)
|
||||||
|
replayer.destroy();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 配置参数
|
#### 配置参数
|
||||||
@@ -384,6 +387,7 @@ replayer.on(EVENT_NAME, (payload) => {
|
|||||||
| mouse-interaction | 回放鼠标交互事件 | { type, target } |
|
| mouse-interaction | 回放鼠标交互事件 | { type, target } |
|
||||||
| event-cast | 回放 event | event |
|
| event-cast | 回放 event | event |
|
||||||
| custom-event | 回放自定义事件 | event |
|
| custom-event | 回放自定义事件 | event |
|
||||||
|
| destroy | 销毁播放器 | - |
|
||||||
|
|
||||||
使用 `rrweb-player` 时,也可以通过 `addEventListener` API 使用相同的事件功能,并且会获得 3 个额外的事件:
|
使用 `rrweb-player` 时,也可以通过 `addEventListener` API 使用相同的事件功能,并且会获得 3 个额外的事件:
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ export class Replayer {
|
|||||||
this.rebuildFullSnapshot(
|
this.rebuildFullSnapshot(
|
||||||
firstFullsnapshot as fullSnapshotEvent & { timestamp: number },
|
firstFullsnapshot as fullSnapshotEvent & { timestamp: number },
|
||||||
);
|
);
|
||||||
this.iframe.contentWindow!.scrollTo(
|
this.iframe.contentWindow?.scrollTo(
|
||||||
(firstFullsnapshot as fullSnapshotEvent).data.initialOffset,
|
(firstFullsnapshot as fullSnapshotEvent).data.initialOffset,
|
||||||
);
|
);
|
||||||
}, 1);
|
}, 1);
|
||||||
@@ -441,12 +441,22 @@ export class Replayer {
|
|||||||
|
|
||||||
public resume(timeOffset = 0) {
|
public resume(timeOffset = 0) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`The 'resume' will be departed in 1.0. Please use 'play' method which has the same interface.`,
|
`The 'resume' was deprecated in 1.0. Please use 'play' method which has the same interface.`,
|
||||||
);
|
);
|
||||||
this.play(timeOffset);
|
this.play(timeOffset);
|
||||||
this.emitter.emit(ReplayerEvents.Resume);
|
this.emitter.emit(ReplayerEvents.Resume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Totally destroy this replayer and please be careful that this operation is irreversible.
|
||||||
|
* Memory occupation can be released by removing all references to this replayer.
|
||||||
|
*/
|
||||||
|
public destroy() {
|
||||||
|
this.pause();
|
||||||
|
this.config.root.removeChild(this.wrapper);
|
||||||
|
this.emitter.emit(ReplayerEvents.Destroy);
|
||||||
|
}
|
||||||
|
|
||||||
public startLive(baselineTime?: number) {
|
public startLive(baselineTime?: number) {
|
||||||
this.service.send({ type: 'TO_LIVE', payload: { baselineTime } });
|
this.service.send({ type: 'TO_LIVE', payload: { baselineTime } });
|
||||||
}
|
}
|
||||||
@@ -590,7 +600,7 @@ export class Replayer {
|
|||||||
this.firstFullSnapshot = true;
|
this.firstFullSnapshot = true;
|
||||||
}
|
}
|
||||||
this.rebuildFullSnapshot(event, isSync);
|
this.rebuildFullSnapshot(event, isSync);
|
||||||
this.iframe.contentWindow!.scrollTo(event.data.initialOffset);
|
this.iframe.contentWindow?.scrollTo(event.data.initialOffset);
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case EventType.IncrementalSnapshot:
|
case EventType.IncrementalSnapshot:
|
||||||
@@ -611,6 +621,7 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
if (this.isUserInteraction(_event)) {
|
if (this.isUserInteraction(_event)) {
|
||||||
if (
|
if (
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
_event.delay! - event.delay! >
|
_event.delay! - event.delay! >
|
||||||
SKIP_TIME_THRESHOLD *
|
SKIP_TIME_THRESHOLD *
|
||||||
this.speedService.state.context.timer.speed
|
this.speedService.state.context.timer.speed
|
||||||
@@ -622,6 +633,7 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
if (this.nextUserInteractionEvent) {
|
if (this.nextUserInteractionEvent) {
|
||||||
const skipTime =
|
const skipTime =
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
this.nextUserInteractionEvent.delay! - event.delay!;
|
this.nextUserInteractionEvent.delay! - event.delay!;
|
||||||
const payload = {
|
const payload = {
|
||||||
speed: Math.min(
|
speed: Math.min(
|
||||||
@@ -742,7 +754,7 @@ export class Replayer {
|
|||||||
styleEl,
|
styleEl,
|
||||||
getDefaultSN(styleEl, this.virtualDom.unserializedId),
|
getDefaultSN(styleEl, this.virtualDom.unserializedId),
|
||||||
);
|
);
|
||||||
(documentElement as RRElement)!.insertBefore(styleEl, head as RRElement);
|
(documentElement as RRElement).insertBefore(styleEl, head as RRElement);
|
||||||
for (let idx = 0; idx < injectStylesRules.length; idx++) {
|
for (let idx = 0; idx < injectStylesRules.length; idx++) {
|
||||||
// push virtual styles
|
// push virtual styles
|
||||||
styleEl.rules.push({
|
styleEl.rules.push({
|
||||||
@@ -753,12 +765,12 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const styleEl = document.createElement('style');
|
const styleEl = document.createElement('style');
|
||||||
(documentElement as HTMLElement)!.insertBefore(
|
(documentElement as HTMLElement).insertBefore(
|
||||||
styleEl,
|
styleEl,
|
||||||
head as HTMLHeadElement,
|
head as HTMLHeadElement,
|
||||||
);
|
);
|
||||||
for (let idx = 0; idx < injectStylesRules.length; idx++) {
|
for (let idx = 0; idx < injectStylesRules.length; idx++) {
|
||||||
styleEl.sheet!.insertRule(injectStylesRules[idx], idx);
|
styleEl.sheet?.insertRule(injectStylesRules[idx], idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -992,6 +1004,7 @@ export class Replayer {
|
|||||||
doAction() {
|
doAction() {
|
||||||
//
|
//
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
delay: e.delay! - d.positions[0]?.timeOffset,
|
delay: e.delay! - d.positions[0]?.timeOffset,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1708,14 +1721,14 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
const sn = this.mirror.getMeta(target);
|
const sn = this.mirror.getMeta(target);
|
||||||
if (target === this.iframe.contentDocument) {
|
if (target === this.iframe.contentDocument) {
|
||||||
this.iframe.contentWindow!.scrollTo({
|
this.iframe.contentWindow?.scrollTo({
|
||||||
top: d.y,
|
top: d.y,
|
||||||
left: d.x,
|
left: d.x,
|
||||||
behavior: isSync ? 'auto' : 'smooth',
|
behavior: isSync ? 'auto' : 'smooth',
|
||||||
});
|
});
|
||||||
} else if (sn?.type === NodeType.Document) {
|
} else if (sn?.type === NodeType.Document) {
|
||||||
// nest iframe content document
|
// nest iframe content document
|
||||||
(target as Document).defaultView!.scrollTo({
|
(target as Document).defaultView?.scrollTo({
|
||||||
top: d.y,
|
top: d.y,
|
||||||
left: d.x,
|
left: d.x,
|
||||||
behavior: isSync ? 'auto' : 'smooth',
|
behavior: isSync ? 'auto' : 'smooth',
|
||||||
|
|||||||
@@ -743,6 +743,7 @@ export enum ReplayerEvents {
|
|||||||
Flush = 'flush',
|
Flush = 'flush',
|
||||||
StateChange = 'state-change',
|
StateChange = 'state-change',
|
||||||
PlayBack = 'play-back',
|
PlayBack = 'play-back',
|
||||||
|
Destroy = 'destroy',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type KeepIframeSrcFn = (src: string) => boolean;
|
export type KeepIframeSrcFn = (src: string) => boolean;
|
||||||
|
|||||||
@@ -701,4 +701,21 @@ describe('replayer', function () {
|
|||||||
|
|
||||||
await assertDomSnapshot(page);
|
await assertDomSnapshot(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should destroy the replayer after calling destroy()', async () => {
|
||||||
|
await page.evaluate(`events = ${JSON.stringify(events)}`);
|
||||||
|
await page.evaluate(`
|
||||||
|
const { Replayer } = rrweb;
|
||||||
|
let replayer = new Replayer(events);
|
||||||
|
replayer.play();
|
||||||
|
`);
|
||||||
|
|
||||||
|
const replayerWrapperClassName = 'replayer-wrapper';
|
||||||
|
let wrapper = await page.$(`.${replayerWrapperClassName}`);
|
||||||
|
expect(wrapper).not.toBeNull();
|
||||||
|
|
||||||
|
await page.evaluate(`replayer.destroy(); replayer = null;`);
|
||||||
|
wrapper = await page.$(`.${replayerWrapperClassName}`);
|
||||||
|
expect(wrapper).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user