[Fizz] Unify preamble only fields to save a field (#35068)

Stacked on #35067.

Same idea of saving a field on the SuspenseBoundary in the common case.
The case where they can have a preamble is rare.
This commit is contained in:
Sebastian Markbåge
2025-11-07 09:19:19 -05:00
committed by GitHub
parent 1e986f514f
commit fa50caf5f8

View File

@@ -255,8 +255,7 @@ type SuspenseBoundary = {
fallbackAbortableTasks: Set<Task>, // used to cancel task on the fallback if the boundary completes or gets canceled.
contentState: HoistableState,
fallbackState: HoistableState,
contentPreamble: null | Preamble,
fallbackPreamble: null | Preamble,
preamble: null | Preamble,
tracked: null | {
contentKeyPath: null | KeyNode, // used to track the path for replay nodes
fallbackNode: null | ReplayNode, // used to track the fallback for replay nodes
@@ -275,7 +274,7 @@ type RenderTask = {
ping: () => void,
blockedBoundary: Root | SuspenseBoundary,
blockedSegment: Segment, // the segment we'll write to
blockedPreamble: null | Preamble,
blockedPreamble: null | PreambleState,
hoistableState: null | HoistableState, // Boundary state we'll mutate while rendering. This may not equal the state of the blockedBoundary
abortSet: Set<Task>, // the abortable set that this task belongs to
keyPath: Root | KeyNode, // the path of all parent keys currently rendering
@@ -398,7 +397,17 @@ export opaque type Request = {
didWarnForKey?: null | WeakSet<ComponentStackNode>,
};
type Preamble = PreambleState;
type Preamble = {
content: PreambleState,
fallback: PreambleState,
};
function createPreamble(): Preamble {
return {
content: createPreambleState(),
fallback: createPreambleState(),
};
}
// This is a default heuristic for how to split up the HTML content into progressive
// loading. Our goal is to be able to display additional new content about every 500ms.
@@ -466,7 +475,7 @@ function isEligibleForOutlining(
// For boundaries that can possibly contribute to the preamble we don't want to outline
// them regardless of their size since the fallbacks should only be emitted if we've
// errored the boundary.
boundary.contentPreamble === null
boundary.preamble === null
);
}
@@ -786,8 +795,7 @@ function createSuspenseBoundary(
request: Request,
row: null | SuspenseListRow,
fallbackAbortableTasks: Set<Task>,
contentPreamble: null | Preamble,
fallbackPreamble: null | Preamble,
preamble: null | Preamble,
defer: boolean,
): SuspenseBoundary {
const boundary: SuspenseBoundary = {
@@ -803,8 +811,7 @@ function createSuspenseBoundary(
errorDigest: null,
contentState: createHoistableState(),
fallbackState: createHoistableState(),
contentPreamble,
fallbackPreamble,
preamble,
tracked: null,
};
if (__DEV__) {
@@ -838,7 +845,7 @@ function createRenderTask(
childIndex: number,
blockedBoundary: Root | SuspenseBoundary,
blockedSegment: Segment,
blockedPreamble: null | Preamble,
blockedPreamble: null | PreambleState,
hoistableState: null | HoistableState,
abortSet: Set<Task>,
keyPath: Root | KeyNode,
@@ -1290,8 +1297,7 @@ function renderSuspenseBoundary(
request,
task.row,
fallbackAbortSet,
createPreambleState(),
createPreambleState(),
createPreamble(),
defer,
);
} else {
@@ -1300,7 +1306,6 @@ function renderSuspenseBoundary(
task.row,
fallbackAbortSet,
null,
null,
defer,
);
}
@@ -1365,7 +1370,8 @@ function renderSuspenseBoundary(
}
task.blockedSegment = boundarySegment;
task.blockedPreamble = newBoundary.fallbackPreamble;
task.blockedPreamble =
newBoundary.preamble === null ? null : newBoundary.preamble.fallback;
task.keyPath = fallbackKeyPath;
task.formatContext = getSuspenseFallbackFormatContext(
request.resumableState,
@@ -1409,7 +1415,7 @@ function renderSuspenseBoundary(
-1,
newBoundary,
contentRootSegment,
newBoundary.contentPreamble,
newBoundary.preamble === null ? null : newBoundary.preamble.content,
newBoundary.contentState,
task.abortSet,
keyPath,
@@ -1440,7 +1446,8 @@ function renderSuspenseBoundary(
// context switching. We just need to temporarily switch which boundary and which segment
// we're writing to. If something suspends, it'll spawn new suspended task with that context.
task.blockedBoundary = newBoundary;
task.blockedPreamble = newBoundary.contentPreamble;
task.blockedPreamble =
newBoundary.preamble === null ? null : newBoundary.preamble.content;
task.hoistableState = newBoundary.contentState;
task.blockedSegment = contentRootSegment;
task.keyPath = keyPath;
@@ -1548,7 +1555,7 @@ function renderSuspenseBoundary(
-1,
parentBoundary,
boundarySegment,
newBoundary.fallbackPreamble,
newBoundary.preamble === null ? null : newBoundary.preamble.fallback,
newBoundary.fallbackState,
fallbackAbortSet,
fallbackKeyPath,
@@ -1602,8 +1609,7 @@ function replaySuspenseBoundary(
request,
task.row,
fallbackAbortSet,
createPreambleState(),
createPreambleState(),
createPreamble(),
defer,
);
} else {
@@ -1612,7 +1618,6 @@ function replaySuspenseBoundary(
task.row,
fallbackAbortSet,
null,
null,
defer,
);
}
@@ -4372,7 +4377,7 @@ function erroredTask(
if (
request.pendingRootTasks === 0 &&
request.trackedPostpones === null &&
boundary.contentPreamble !== null
boundary.preamble !== null
) {
// The root is complete and this boundary may contribute part of the preamble.
// We eagerly attempt to prepare the preamble here because we expect most requests
@@ -4414,7 +4419,6 @@ function abortRemainingSuspenseBoundary(
null,
new Set(),
null,
null,
false,
);
resumedBoundary.parentFlushed = true;
@@ -4894,7 +4898,7 @@ function finishedTask(
if (
request.pendingRootTasks === 0 &&
request.trackedPostpones === null &&
boundary.contentPreamble !== null
boundary.preamble !== null
) {
// The root is complete and this boundary may contribute part of the preamble.
// We eagerly attempt to prepare the preamble here because we expect most requests
@@ -5271,10 +5275,8 @@ function preparePreambleFromSegment(
);
}
const preamble = boundary.contentPreamble;
const fallbackPreamble = boundary.fallbackPreamble;
if (preamble === null || fallbackPreamble === null) {
const preamble = boundary.preamble;
if (preamble === null) {
// This boundary cannot have a preamble so it can't block the flushing of
// the preamble.
return false;
@@ -5286,7 +5288,7 @@ function preparePreambleFromSegment(
case COMPLETED: {
// This boundary is complete. It might have inner boundaries which are pending
// and able to provide a preamble so we have to check it's children
hoistPreambleState(request.renderState, preamble);
hoistPreambleState(request.renderState, preamble.content);
// We track this boundary's byteSize on the request since it will always flush with
// the request since it may contribute to the preamble
request.byteSize += boundary.byteSize;
@@ -5315,7 +5317,7 @@ function preparePreambleFromSegment(
case CLIENT_RENDERED: {
if (segment.status === COMPLETED) {
// This boundary is errored so if it contains a preamble we should include it
hoistPreambleState(request.renderState, fallbackPreamble);
hoistPreambleState(request.renderState, preamble.fallback);
return preparePreambleFromSubtree(
request,
segment,