feat: add LIS-based move detection for keyed children reconciliation
This commit is contained in:
@@ -156,6 +156,46 @@ export function resetUpdateQueue(): void {
|
||||
flushScheduled = false;
|
||||
}
|
||||
|
||||
export function longestIncreasingSubsequence(arr: number[]): number[] {
|
||||
if (arr.length === 0) return [];
|
||||
|
||||
const tails: number[] = [];
|
||||
const tailsIdx: number[] = [];
|
||||
const prev: number[] = new Array(arr.length).fill(-1);
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const val = arr[i]!;
|
||||
let lo = 0;
|
||||
let hi = tails.length;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi) >>> 1;
|
||||
if (tails[mid]! < val) {
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
if (lo > 0) {
|
||||
prev[i] = tailsIdx[lo - 1]!;
|
||||
}
|
||||
if (lo === tails.length) {
|
||||
tails.push(val);
|
||||
tailsIdx.push(i);
|
||||
} else {
|
||||
tails[lo] = val;
|
||||
tailsIdx[lo] = i;
|
||||
}
|
||||
}
|
||||
|
||||
const result: number[] = new Array(tails.length);
|
||||
let k = tailsIdx[tails.length - 1]!;
|
||||
for (let j = tails.length - 1; j >= 0; j--) {
|
||||
result[j] = k;
|
||||
k = prev[k]!;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface MatchedChild<I> {
|
||||
oldFiber: Fiber<I>;
|
||||
newChild: UElement;
|
||||
@@ -166,6 +206,7 @@ export interface ChildClassification<I> {
|
||||
matched: MatchedChild<I>[];
|
||||
added: { newChild: UElement; index: number }[];
|
||||
removed: Fiber<I>[];
|
||||
moves: Map<number, Fiber<I>>;
|
||||
}
|
||||
|
||||
export function reconcileChildren<I>(
|
||||
@@ -267,5 +308,16 @@ export function reconcileChildren<I>(
|
||||
}
|
||||
}
|
||||
|
||||
return { matched, added, removed };
|
||||
const oldIndices = matched.map((m) => oldFibers.indexOf(m.oldFiber));
|
||||
const lisPositions = longestIncreasingSubsequence(oldIndices);
|
||||
const lisSet = new Set(lisPositions);
|
||||
|
||||
const moves = new Map<number, Fiber<I>>();
|
||||
for (let i = 0; i < matched.length; i++) {
|
||||
if (!lisSet.has(i)) {
|
||||
moves.set(i, matched[i]!.oldFiber);
|
||||
}
|
||||
}
|
||||
|
||||
return { matched, added, removed, moves };
|
||||
}
|
||||
Reference in New Issue
Block a user