refactor(core): do not serialize parent block id for top level blocks (#59190)

This commit updates incremental hydration-related annotation logic to avoid serializing parent block id when it's `null` (for top-level blocks).

PR Close #59190
This commit is contained in:
Andrew Kushnir
2024-12-13 13:26:15 -08:00
committed by kirjs
parent 2872a0cd1f
commit f7ff00084d
4 changed files with 41 additions and 8 deletions
+5 -1
View File
@@ -405,7 +405,6 @@ function serializeLContainer(
// Add defer block into info context.deferBlocks
const deferBlockInfo: SerializedDeferBlock = {
[DEFER_PARENT_BLOCK_ID]: parentDeferBlockId,
[NUM_ROOT_NODES]: rootNodes.length,
[DEFER_BLOCK_STATE]: lDetails[CURRENT_DEFER_BLOCK_STATE],
};
@@ -415,6 +414,11 @@ function serializeLContainer(
deferBlockInfo[DEFER_HYDRATE_TRIGGERS] = serializedTriggers;
}
if (parentDeferBlockId !== null) {
// Serialize parent id only when it's present.
deferBlockInfo[DEFER_PARENT_BLOCK_ID] = parentDeferBlockId;
}
context.deferBlocks.set(deferBlockId, deferBlockInfo);
const node = unwrapRNode(lContainer);
+1 -1
View File
@@ -158,7 +158,7 @@ export interface SerializedDeferBlock {
/**
* This contains the unique id of this defer block's parent, if it exists.
*/
[DEFER_PARENT_BLOCK_ID]: string | null;
[DEFER_PARENT_BLOCK_ID]?: string;
/**
* This field represents a status, based on the `DeferBlockState` enum.
+1 -1
View File
@@ -571,7 +571,7 @@ export function getParentBlockHydrationQueue(
const deferBlockParents = transferState.get(NGH_DEFER_BLOCKS_KEY, {});
let isTopMostDeferBlock = false;
let currentBlockId: string | null = deferBlockId;
let currentBlockId: string | undefined = deferBlockId;
let parentBlockPromise: Promise<void> | null = null;
const hydrationQueue: string[] = [];
@@ -222,7 +222,7 @@ describe('platform-server partial hydration integration', () => {
const ssrContents = getAppContents(html);
expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":2,"s":2}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":2,"s":2,"p":"d0"}}',
);
});
@@ -285,9 +285,38 @@ describe('platform-server partial hydration integration', () => {
const ssrContents = getAppContents(html);
expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":2,"s":2,"t":[2]}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":2,"s":2,"t":[2],"p":"d0"}}',
);
});
it('should not include parent id in serialized data for top-level `@defer` blocks', async () => {
@Component({
selector: 'app',
template: `
@defer (on viewport; hydrate on interaction) {
Hello world!
} @placeholder {
<span>Placeholder</span>
}
`,
})
class SimpleComponent {}
const appId = 'custom-app-id';
const providers = [{provide: APP_ID, useValue: appId}];
const hydrationFeatures = () => [withIncrementalHydration()];
const html = await ssr(SimpleComponent, {
envProviders: providers,
hydrationFeatures,
});
const ssrContents = getAppContents(html);
// Assert that the serialized data doesn't contain the "p" field,
// which contains parent id (which is not needed for top-level blocks).
expect(ssrContents).toContain('"__nghDeferData__":{"d0":{"r":1,"s":2}}}');
});
});
describe('basic hydration behavior', () => {
@@ -347,7 +376,7 @@ describe('platform-server partial hydration integration', () => {
expect(ssrContents).toContain('<p jsaction="click:;keydown:;" ngb="d1');
// There is an extra annotation in the TransferState data.
expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":1,"s":2}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":1,"s":2,"p":"d0"}}',
);
// Outer defer block is rendered.
expect(ssrContents).toContain('Main defer block rendered');
@@ -460,7 +489,7 @@ describe('platform-server partial hydration integration', () => {
expect(ssrContents).toContain('<p jsaction="click:;keydown:;" ngb="d1');
// There is an extra annotation in the TransferState data.
expect(ssrContents).toContain(
'"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2},"d1":{"p":"d0","r":1,"s":2}}',
'"__nghDeferData__":{"d0":{"r":1,"s":2},"d1":{"r":1,"s":2,"p":"d0"}}',
);
// Outer defer block is rendered.
expect(ssrContents).toContain('Main defer block rendered');
@@ -569,7 +598,7 @@ describe('platform-server partial hydration integration', () => {
// <p> is inside a nested defer block -> different namespace.
// expect(ssrContents).toContain('<p jsaction="click:;" ngb="d1');
// There is an extra annotation in the TransferState data.
expect(ssrContents).toContain('"__nghDeferData__":{"d0":{"p":null,"r":1,"s":2}}');
expect(ssrContents).toContain('"__nghDeferData__":{"d0":{"r":1,"s":2}}');
// Outer defer block is rendered.
expect(ssrContents).toContain('Main defer block rendered');
// Inner defer block should only display placeholder.