ref: isParentRemoved to cache subtree (#1543)

* ref: isParentRemoved to cache subtree
* ref: cache at insertion too
* ref: remove wrapper function

---------

Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
This commit is contained in:
Jonas
2026-04-01 12:00:00 +08:00
committed by GitHub
parent f664e6478b
commit 464554039d
2 changed files with 31 additions and 19 deletions

View File

@@ -0,0 +1,5 @@
---
"rrweb": minor
---
Optimize isParentRemoved check

View File

@@ -169,6 +169,7 @@ export default class MutationBuffer {
private addedSet = new Set<Node>(); private addedSet = new Set<Node>();
private movedSet = new Set<Node>(); private movedSet = new Set<Node>();
private droppedSet = new Set<Node>(); private droppedSet = new Set<Node>();
private removesSubTreeCache = new Set<Node>();
private mutationCb: observerParam['mutationCb']; private mutationCb: observerParam['mutationCb'];
private blockClass: observerParam['blockClass']; private blockClass: observerParam['blockClass'];
@@ -367,7 +368,7 @@ export default class MutationBuffer {
for (const n of this.movedSet) { for (const n of this.movedSet) {
if ( if (
isParentRemoved(this.removes, n, this.mirror) && isParentRemoved(this.removesSubTreeCache, n, this.mirror) &&
!this.movedSet.has(dom.parentNode(n)!) !this.movedSet.has(dom.parentNode(n)!)
) { ) {
continue; continue;
@@ -378,7 +379,7 @@ export default class MutationBuffer {
for (const n of this.addedSet) { for (const n of this.addedSet) {
if ( if (
!isAncestorInSet(this.droppedSet, n) && !isAncestorInSet(this.droppedSet, n) &&
!isParentRemoved(this.removes, n, this.mirror) !isParentRemoved(this.removesSubTreeCache, n, this.mirror)
) { ) {
pushAdd(n); pushAdd(n);
} else if (isAncestorInSet(this.movedSet, n)) { } else if (isAncestorInSet(this.movedSet, n)) {
@@ -514,6 +515,7 @@ export default class MutationBuffer {
this.addedSet = new Set<Node>(); this.addedSet = new Set<Node>();
this.movedSet = new Set<Node>(); this.movedSet = new Set<Node>();
this.droppedSet = new Set<Node>(); this.droppedSet = new Set<Node>();
this.removesSubTreeCache = new Set<Node>();
this.movedMap = {}; this.movedMap = {};
this.mutationCb(payload); this.mutationCb(payload);
@@ -740,6 +742,7 @@ export default class MutationBuffer {
? true ? true
: undefined, : undefined,
}); });
processRemoves(n, this.removesSubTreeCache);
} }
this.mapRemoves.push(n); this.mapRemoves.push(n);
}); });
@@ -803,29 +806,33 @@ function deepDelete(addsSet: Set<Node>, n: Node) {
dom.childNodes(n).forEach((childN) => deepDelete(addsSet, childN)); dom.childNodes(n).forEach((childN) => deepDelete(addsSet, childN));
} }
function isParentRemoved( function processRemoves(n: Node, cache: Set<Node>) {
removes: removedNodeMutation[], const queue = [n];
n: Node,
mirror: Mirror, while (queue.length) {
): boolean { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (removes.length === 0) return false; const next = queue.pop()!;
if (cache.has(next)) continue;
cache.add(next);
dom.childNodes(next).forEach((n) => queue.push(n));
}
return;
}
function isParentRemoved(removes: Set<Node>, n: Node, mirror: Mirror): boolean {
if (removes.size === 0) return false;
return _isParentRemoved(removes, n, mirror); return _isParentRemoved(removes, n, mirror);
} }
function _isParentRemoved( function _isParentRemoved(
removes: removedNodeMutation[], removes: Set<Node>,
n: Node, n: Node,
mirror: Mirror, _mirror: Mirror,
): boolean { ): boolean {
let node: ParentNode | null = dom.parentNode(n); const node: ParentNode | null = dom.parentNode(n);
while (node) { if (!node) return false;
const parentId = mirror.getId(node); return removes.has(node);
if (removes.some((r) => r.id === parentId)) {
return true;
}
node = dom.parentNode(node);
}
return false;
} }
function isAncestorInSet(set: Set<Node>, n: Node): boolean { function isAncestorInSet(set: Set<Node>, n: Node): boolean {