feat: add Value.Equal bail-out check before reconciliation
Add TypeBox Value.Equal deep-comparison as first optimization layer in reconcileProps. When a fiber's cached node is deep-equal to the next node, skip prepareUpdate, commitUpdate, and children reconciliation entirely. New cachedNode field on Fiber stores the last reconciled node for comparison.
This commit is contained in:
@@ -70,6 +70,7 @@ export function createRoot<TTag extends string, Instance, RootCtx>(
|
||||
effect: null,
|
||||
signalDisposers: [],
|
||||
prevProps: null,
|
||||
cachedNode: node,
|
||||
};
|
||||
if (parentFiber) parentFiber.children.push(fiber);
|
||||
return fiber;
|
||||
@@ -106,6 +107,7 @@ export function createRoot<TTag extends string, Instance, RootCtx>(
|
||||
effect: null,
|
||||
signalDisposers: [],
|
||||
prevProps: null,
|
||||
cachedNode: node,
|
||||
};
|
||||
|
||||
for (const child of el.children) {
|
||||
@@ -132,6 +134,7 @@ export function createRoot<TTag extends string, Instance, RootCtx>(
|
||||
effect: null,
|
||||
signalDisposers: [],
|
||||
prevProps: null,
|
||||
cachedNode: node,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -158,6 +161,7 @@ export function createRoot<TTag extends string, Instance, RootCtx>(
|
||||
effect: null,
|
||||
signalDisposers: [],
|
||||
prevProps: null,
|
||||
cachedNode: node,
|
||||
};
|
||||
|
||||
for (const child of el.children) {
|
||||
@@ -281,6 +285,7 @@ export function createRoot<TTag extends string, Instance, RootCtx>(
|
||||
effect: null,
|
||||
signalDisposers: [],
|
||||
prevProps: null,
|
||||
cachedNode: null,
|
||||
};
|
||||
const payloadChildren = isURoot(node) ? (node as URoot).children : [node];
|
||||
for (const child of payloadChildren) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { UNode } from "../core/schema.js";
|
||||
|
||||
export interface Fiber<I> {
|
||||
instance: I;
|
||||
tag: string;
|
||||
@@ -8,6 +10,7 @@ export interface Fiber<I> {
|
||||
effect: Effect<I> | null;
|
||||
signalDisposers: (() => void)[];
|
||||
prevProps: Record<string, unknown> | null;
|
||||
cachedNode: UNode | null;
|
||||
}
|
||||
|
||||
export type Effect<I> =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { effect } from "@preact/signals-core";
|
||||
import { Value } from "@alkdev/typebox/value";
|
||||
import type { Fiber, Effect } from "./fiber.js";
|
||||
import type { HostConfig } from "./config.js";
|
||||
import type { UNode, UElement } from "../core/schema.js";
|
||||
@@ -59,6 +60,10 @@ export function reconcileProps<I>(
|
||||
host: HostConfig<string, I, unknown>,
|
||||
ctx: unknown,
|
||||
): void {
|
||||
if (fiber.cachedNode !== null && Value.Equal(fiber.cachedNode, nextNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUPrimitive(nextNode)) {
|
||||
if (fiber.tag === "#text") {
|
||||
const text = nextNode === null ? "" : String(nextNode);
|
||||
@@ -78,6 +83,7 @@ export function reconcileProps<I>(
|
||||
}
|
||||
}
|
||||
}
|
||||
fiber.cachedNode = nextNode;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,6 +93,7 @@ export function reconcileProps<I>(
|
||||
for (let i = 0; i < count; i++) {
|
||||
reconcileProps(fiber.children[i]!, rootChildren[i]!, host, ctx);
|
||||
}
|
||||
fiber.cachedNode = nextNode;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,6 +103,7 @@ export function reconcileProps<I>(
|
||||
const component = el.type as (props: Record<string, unknown>) => UNode;
|
||||
const out = component({ ...el.props, children: el.children });
|
||||
reconcileProps(fiber, out, host, ctx);
|
||||
fiber.cachedNode = nextNode;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -120,6 +128,7 @@ export function reconcileProps<I>(
|
||||
for (let i = 0; i < count; i++) {
|
||||
reconcileProps(fiber.children[i]!, el.children[i]!, host, ctx);
|
||||
}
|
||||
fiber.cachedNode = nextNode;
|
||||
}
|
||||
|
||||
export function commitEffects<I>(
|
||||
|
||||
Reference in New Issue
Block a user