Skip to content

Commit 0f2f7ec

Browse files
thePunderWomanalxhub
authored andcommitted
refactor(core): cleanup incremental hydration code (angular#58363)
This cleans up some minor issues with the incremental hydration implementation and should make maintaining it a little easier. PR Close angular#58363
1 parent 35d7ca5 commit 0f2f7ec

File tree

10 files changed

+102
-84
lines changed

10 files changed

+102
-84
lines changed

packages/core/src/defer/cleanup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
PREFETCH_TRIGGER_CLEANUP_FNS,
1414
TRIGGER_CLEANUP_FNS,
1515
TriggerType,
16-
UNIQUE_SSR_ID,
16+
SSR_UNIQUE_ID,
1717
} from './interfaces';
1818
import {DeferBlockRegistry} from './registry';
1919

@@ -57,7 +57,7 @@ export function invokeAllTriggerCleanupFns(
5757
// TODO(incremental-hydration): cleanup functions are invoked in multiple places
5858
// should we centralize where cleanup functions are invoked to this registry?
5959
if (registry !== null) {
60-
registry.invokeCleanupFns(lDetails[UNIQUE_SSR_ID]!);
60+
registry.invokeCleanupFns(lDetails[SSR_UNIQUE_ID]!);
6161
}
6262

6363
invokeTriggerCleanupFns(TriggerType.Prefetch, lDetails);

packages/core/src/defer/instructions.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,12 @@ import {
7979
LOADING_AFTER_CLEANUP_FN,
8080
NEXT_DEFER_BLOCK_STATE,
8181
ON_COMPLETE_FNS,
82-
SSR_STATE,
82+
SSR_BLOCK_STATE,
8383
STATE_IS_FROZEN_UNTIL,
8484
TDeferBlockDetails,
8585
TriggerType,
8686
DeferBlock,
87-
UNIQUE_SSR_ID,
87+
SSR_UNIQUE_ID,
8888
} from './interfaces';
8989
import {onTimer, scheduleTimerTrigger} from './timer_scheduler';
9090
import {
@@ -144,7 +144,8 @@ function shouldTriggerWhenOnClient(
144144
if (!isPlatformBrowser(injector)) {
145145
return false;
146146
}
147-
const isServerRendered = lDetails[SSR_STATE] && lDetails[SSR_STATE] === DeferBlockState.Complete;
147+
const isServerRendered =
148+
lDetails[SSR_BLOCK_STATE] && lDetails[SSR_BLOCK_STATE] === DeferBlockState.Complete;
148149
const hasHydrateTriggers = tDetails.hydrateTriggers && tDetails.hydrateTriggers.size > 0;
149150
if (hasHydrateTriggers && isServerRendered && isIncrementalHydrationEnabled(injector)) {
150151
return false;
@@ -280,13 +281,12 @@ export function ɵɵdefer(
280281
// In client-only mode, this function is a noop.
281282
populateDehydratedViewsInLContainer(lContainer, tNode, lView);
282283

283-
let ssrState = null;
284-
let uniqueId: string | null = null;
284+
let ssrBlockState = null;
285+
let ssrUniqueId: string | null = null;
285286
if (lContainer[DEHYDRATED_VIEWS]?.length > 0) {
286-
// TODO(incremental-hydration): this is a hack, we should serialize defer
287287
const info = lContainer[DEHYDRATED_VIEWS][0].data;
288-
uniqueId = info[DEFER_BLOCK_ID] ?? null;
289-
ssrState = info[SERIALIZED_DEFER_BLOCK_STATE];
288+
ssrUniqueId = info[DEFER_BLOCK_ID] ?? null;
289+
ssrBlockState = info[SERIALIZED_DEFER_BLOCK_STATE];
290290
}
291291

292292
// Init instance-specific defer details and store it.
@@ -297,21 +297,21 @@ export function ɵɵdefer(
297297
null, // LOADING_AFTER_CLEANUP_FN
298298
null, // TRIGGER_CLEANUP_FNS
299299
null, // PREFETCH_TRIGGER_CLEANUP_FNS
300-
uniqueId, // UNIQUE_ID
301-
ssrState, // SSR_STATE
300+
ssrUniqueId, // SSR_UNIQUE_ID
301+
ssrBlockState, // SSR_BLOCK_STATE
302302
null, // ON_COMPLETE_FNS
303303
null, // HYDRATE_TRIGGER_CLEANUP_FNS
304304
];
305305
setLDeferBlockDetails(lView, adjustedIndex, lDetails);
306306

307307
let registry: DeferBlockRegistry | null = null;
308-
if (uniqueId !== null) {
308+
if (ssrUniqueId !== null) {
309309
// TODO(incremental-hydration): explore how we can make
310310
// `DeferBlockRegistry` tree-shakable for client-only cases.
311311
registry = injector.get(DeferBlockRegistry);
312312

313313
// Also store this defer block in the registry.
314-
registry.add(uniqueId, {lView, tNode, lContainer});
314+
registry.add(ssrUniqueId, {lView, tNode, lContainer});
315315
}
316316

317317
const cleanupTriggersFn = () => invokeAllTriggerCleanupFns(lDetails, registry);
@@ -415,7 +415,7 @@ export function ɵɵdeferHydrateWhen(rawValue: unknown) {
415415
// state.
416416
incrementallyHydrateFromBlockName(
417417
injector,
418-
getLDeferBlockDetails(lView, tNode)[UNIQUE_SSR_ID]!,
418+
getLDeferBlockDetails(lView, tNode)[SSR_UNIQUE_ID]!,
419419
(deferBlock: DeferBlock) => triggerAndWaitForCompletion(deferBlock),
420420
);
421421
}
@@ -534,7 +534,7 @@ export function ɵɵdeferHydrateOnImmediate() {
534534
} else {
535535
incrementallyHydrateFromBlockName(
536536
injector,
537-
lDetails[UNIQUE_SSR_ID]!,
537+
lDetails[SSR_UNIQUE_ID]!,
538538
(deferBlock: DeferBlock) => triggerAndWaitForCompletion(deferBlock),
539539
);
540540
}
@@ -643,6 +643,8 @@ export function ɵɵdeferHydrateOnHover() {
643643
// We are on the server and SSR for defer blocks is enabled.
644644
triggerDeferBlock(lView, tNode);
645645
}
646+
// The actual triggering of hydration on hover is handled by JSAction in
647+
// event_replay.ts.
646648
}
647649

648650
/**
@@ -711,6 +713,8 @@ export function ɵɵdeferHydrateOnInteraction() {
711713
// We are on the server and SSR for defer blocks is enabled.
712714
triggerDeferBlock(lView, tNode);
713715
}
716+
// The actual triggering of hydration on interaction is handled by JSAction in
717+
// event_replay.ts.
714718
}
715719

716720
/**
@@ -780,6 +784,8 @@ export function ɵɵdeferHydrateOnViewport() {
780784
// We are on the server and SSR for defer blocks is enabled.
781785
triggerDeferBlock(lView, tNode);
782786
}
787+
// The actual triggering of hydration on viewport happens in incremental.ts,
788+
// since these instructions won't exist for dehydrated content.
783789
}
784790

785791
/********** Helper functions **********/
@@ -853,7 +859,7 @@ export function scheduleDelayedHydrating(
853859
() =>
854860
incrementallyHydrateFromBlockName(
855861
injector,
856-
lDetails[UNIQUE_SSR_ID]!,
862+
lDetails[SSR_UNIQUE_ID]!,
857863
(deferBlock: DeferBlock) => triggerAndWaitForCompletion(deferBlock),
858864
),
859865
injector,
@@ -896,7 +902,7 @@ export function renderDeferBlockState(
896902

897903
const currentState = lDetails[DEFER_BLOCK_STATE];
898904

899-
const ssrState = lDetails[SSR_STATE];
905+
const ssrState = lDetails[SSR_BLOCK_STATE];
900906
if (ssrState !== null && newState < ssrState) {
901907
return; // trying to render a previous state, exit
902908
}

packages/core/src/defer/interfaces.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ export const STATE_IS_FROZEN_UNTIL = 2;
210210
export const LOADING_AFTER_CLEANUP_FN = 3;
211211
export const TRIGGER_CLEANUP_FNS = 4;
212212
export const PREFETCH_TRIGGER_CLEANUP_FNS = 5;
213-
export const UNIQUE_SSR_ID = 6;
214-
export const SSR_STATE = 7;
213+
export const SSR_UNIQUE_ID = 6;
214+
export const SSR_BLOCK_STATE = 7;
215215
export const ON_COMPLETE_FNS = 8;
216216
export const HYDRATE_TRIGGER_CLEANUP_FNS = 9;
217217

@@ -259,12 +259,12 @@ export interface LDeferBlockDetails extends Array<unknown> {
259259
/**
260260
* Unique id of this defer block assigned during SSR.
261261
*/
262-
[UNIQUE_SSR_ID]: string | null;
262+
[SSR_UNIQUE_ID]: string | null;
263263

264264
/**
265265
* Defer block state after SSR.
266266
*/
267-
[SSR_STATE]: number | null;
267+
[SSR_BLOCK_STATE]: number | null;
268268

269269
/**
270270
* A set of callbacks to be invoked once the main content is rendered.

packages/core/src/defer/registry.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ import {ɵɵdefineInjectable} from '../di';
99
import {DeferBlock} from './interfaces';
1010

1111
// TODO(incremental-hydration): refactor this so that it's not used in CSR cases
12+
/**
13+
* The DeferBlockRegistry is used for incremental hydration purposes. It keeps
14+
* track of the Defer Blocks that need hydration so we can effectively
15+
* navigate up to the top dehydrated defer block and fire appropriate cleanup
16+
* functions post hydration.
17+
*/
1218
export class DeferBlockRegistry {
1319
private registry = new Map<string, DeferBlock>();
1420
private cleanupFns = new Map<string, Function[]>();
15-
add(blockId: string, info: any) {
21+
add(blockId: string, info: DeferBlock) {
1622
this.registry.set(blockId, info);
1723
}
18-
get(blockId: string) {
24+
get(blockId: string): DeferBlock | null {
1925
return this.registry.get(blockId) ?? null;
2026
}
2127
// TODO(incremental-hydration): we need to determine when this should be invoked
@@ -38,14 +44,17 @@ export class DeferBlockRegistry {
3844

3945
invokeCleanupFns(blockId: string) {
4046
// TODO(incremental-hydration): determine if we can safely remove entries from
41-
// the cleanupFns after they've been invoked
47+
// the cleanupFns after they've been invoked. Can we reset
48+
// `this.cleanupFns.get(blockId)`?
4249
const fns = this.cleanupFns.get(blockId) ?? [];
4350
for (let fn of fns) {
4451
fn();
4552
}
4653
}
4754

4855
// Blocks that are being hydrated.
56+
// TODO(incremental-hydration): cleanup task - we currently retain ids post hydration
57+
// and need to determine when we can remove them.
4958
hydrating = new Set();
5059

5160
/** @nocollapse */

packages/core/src/event_delegation_utils.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,9 @@ import {EventContract} from '@angular/core/primitives/event-dispatch';
1111
import {Attribute} from '@angular/core/primitives/event-dispatch';
1212
import {InjectionToken, Injector} from './di';
1313
import {RElement} from './render3/interfaces/renderer_dom';
14-
import {
15-
BLOCK_ELEMENT_MAP,
16-
EVENT_REPLAY_ENABLED_DEFAULT,
17-
IS_EVENT_REPLAY_ENABLED,
18-
} from './hydration/tokens';
19-
import {OnDestroy} from './interface/lifecycle_hooks';
14+
import {BLOCK_ELEMENT_MAP} from './hydration/tokens';
2015

21-
export const BLOCKNAME_ATTRIBUTE = 'ngb';
16+
export const DEFER_BLOCK_SSR_ID_ATTRIBUTE = 'ngb';
2217

2318
declare global {
2419
interface Element {
@@ -41,7 +36,9 @@ export function setJSActionAttributes(
4136
eventTypes: string[],
4237
parentDeferBlockId: string | null = null,
4338
) {
44-
if (!eventTypes.length || nativeElement.nodeType !== Node.ELEMENT_NODE) {
39+
// jsaction attributes specifically should be applied to elements and not comment nodes.
40+
// Comment nodes also have no setAttribute function. So this avoids errors.
41+
if (eventTypes.length === 0 || nativeElement.nodeType !== Node.ELEMENT_NODE) {
4542
return;
4643
}
4744
const existingAttr = nativeElement.getAttribute(Attribute.JSACTION);
@@ -57,7 +54,7 @@ export function setJSActionAttributes(
5754

5855
const blockName = parentDeferBlockId ?? '';
5956
if (blockName !== '' && parts.length > 0) {
60-
nativeElement.setAttribute(BLOCKNAME_ATTRIBUTE, blockName);
57+
nativeElement.setAttribute(DEFER_BLOCK_SSR_ID_ATTRIBUTE, blockName);
6158
}
6259
}
6360

@@ -71,7 +68,7 @@ export const sharedStashFunction = (rEl: RElement, eventType: string, listenerFn
7168
};
7269

7370
export const sharedMapFunction = (rEl: RElement, jsActionMap: Map<string, Set<Element>>) => {
74-
let blockName = rEl.getAttribute(BLOCKNAME_ATTRIBUTE) ?? '';
71+
let blockName = rEl.getAttribute(DEFER_BLOCK_SSR_ID_ATTRIBUTE) ?? '';
7572
const el = rEl as unknown as Element;
7673
const blockSet = jsActionMap.get(blockName) ?? new Set<Element>();
7774
if (!blockSet.has(el)) {
@@ -94,7 +91,7 @@ export function removeListenersFromBlocks(blockNames: string[], injector: Inject
9491

9592
export const removeListeners = (el: Element) => {
9693
el.removeAttribute(Attribute.JSACTION);
97-
el.removeAttribute(BLOCKNAME_ATTRIBUTE);
94+
el.removeAttribute(DEFER_BLOCK_SSR_ID_ATTRIBUTE);
9895
el.__jsaction_fns = undefined;
9996
};
10097

0 commit comments

Comments
 (0)