/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/platform-browser/src/hydration.ts
312 строк
10 KB
Matthieu Riegler
feat(core): add ability to cache resources for SSR
06 май 2026, 19:57
Не верифицирован
06 май 2026, 19:57
5a7c1e6
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {HttpTransferCacheOptions, ɵwithHttpTransferCache} from '@angular/common/http'; import { APP_BOOTSTRAP_LISTENER, ApplicationRef, ɵCACHE_ACTIVE as CACHE_ACTIVE, ɵConsole as Console, ENVIRONMENT_INITIALIZER, EnvironmentProviders, ɵformatRuntimeError as formatRuntimeError, inject, ɵIS_ENABLED_BLOCKING_INITIAL_NAVIGATION as IS_ENABLED_BLOCKING_INITIAL_NAVIGATION, makeEnvironmentProviders, Provider, provideStabilityDebugging, ɵRuntimeError as RuntimeError, ɵwithDomHydration as withDomHydration, ɵwithEventReplay, ɵwithI18nSupport, ɵwithIncrementalHydration, } from '@angular/core'; import {RuntimeErrorCode} from './errors'; /** * The list of features as an enum to uniquely type each `HydrationFeature`. * @see {@link HydrationFeature} * * @publicApi */ export enum HydrationFeatureKind { NoHttpTransferCache, HttpTransferCacheOptions, I18nSupport, EventReplay, IncrementalHydration, NoIncrementalHydration, } /** * Helper type to represent a Hydration feature. * * @publicApi */ export interface HydrationFeature<FeatureKind extends HydrationFeatureKind> { ɵkind: FeatureKind; ɵproviders: Provider[]; } /** * Helper function to create an object that represents a Hydration feature. */ function hydrationFeature<FeatureKind extends HydrationFeatureKind>( ɵkind: FeatureKind, ɵproviders: Provider[] = [], ɵoptions: unknown = {}, ): HydrationFeature<FeatureKind> { return {ɵkind, ɵproviders}; } /** * Disables HTTP transfer cache. Effectively causes HTTP requests to be performed twice: once on the * server and other one on the browser. * * @see [Disabling Caching](guide/ssr#disabling-caching) * * @publicApi */ export function withNoHttpTransferCache(): HydrationFeature<HydrationFeatureKind.NoHttpTransferCache> { // This feature has no providers and acts as a flag that turns off // HTTP transfer cache (which otherwise is turned on by default). return hydrationFeature(HydrationFeatureKind.NoHttpTransferCache); } /** * The function accepts an object, which allows to configure cache parameters, * such as which headers should be included (no headers are included by default), * whether POST requests should be cached or a callback function to determine if a * particular request should be cached. * * @see [Configuring HTTP transfer cache options](guide/ssr#caching-data-when-using-httpclient) * * @publicApi */ export function withHttpTransferCacheOptions( options: HttpTransferCacheOptions, ): HydrationFeature<HydrationFeatureKind.HttpTransferCacheOptions> { // This feature has no providers and acts as a flag to pass options to the HTTP transfer cache. return hydrationFeature( HydrationFeatureKind.HttpTransferCacheOptions, ɵwithHttpTransferCache(options), ); } /** * Enables support for hydrating i18n blocks. * * @publicApi 20.0 */ export function withI18nSupport(): HydrationFeature<HydrationFeatureKind.I18nSupport> { return hydrationFeature(HydrationFeatureKind.I18nSupport, ɵwithI18nSupport()); } /** * Enables support for replaying user events (e.g. `click`s) that happened on a page * before hydration logic has completed. Once an application is hydrated, all captured * events are replayed and relevant event listeners are executed. * * @usageNotes * * Basic example of how you can enable event replay in your application when * `bootstrapApplication` function is used: * ```ts * bootstrapApplication(App, { * providers: [provideClientHydration(withEventReplay())] * }); * ``` * @publicApi * @see {@link provideClientHydration} */ export function withEventReplay(): HydrationFeature<HydrationFeatureKind.EventReplay> { return hydrationFeature(HydrationFeatureKind.EventReplay, ɵwithEventReplay()); } /** * Enables support for incremental hydration using the `hydrate` trigger syntax. * * @usageNotes * * Basic example of how you can enable incremental hydration in your application when * the `bootstrapApplication` function is used: * ```ts * bootstrapApplication(App, { * providers: [provideClientHydration(withIncrementalHydration())] * }); * ``` * @publicApi 20.0 * @see {@link provideClientHydration} * * @deprecated Since v22.0.0, incremental hydration is enabled by default with `provideClientHydration`. * Intent to remove in v24. */ export function withIncrementalHydration(): HydrationFeature<HydrationFeatureKind.IncrementalHydration> { return hydrationFeature(HydrationFeatureKind.IncrementalHydration, ɵwithIncrementalHydration()); } /** * Disables support for incremental hydration (which is enabled by default). * * @publicApi 22.0 * @see {@link provideClientHydration} */ export function withNoIncrementalHydration(): HydrationFeature<HydrationFeatureKind.NoIncrementalHydration> { return hydrationFeature(HydrationFeatureKind.NoIncrementalHydration); } /** * Returns an `ENVIRONMENT_INITIALIZER` token setup with a function * that verifies whether enabledBlocking initial navigation is used in an application * and logs a warning in a console if it's not compatible with hydration. */ function provideEnabledBlockingInitialNavigationDetector(): Provider[] { return [ { provide: ENVIRONMENT_INITIALIZER, useValue: () => { const isEnabledBlockingInitialNavigation = inject(IS_ENABLED_BLOCKING_INITIAL_NAVIGATION, { optional: true, }); if (isEnabledBlockingInitialNavigation) { const console = inject(Console); const message = formatRuntimeError( RuntimeErrorCode.HYDRATION_CONFLICTING_FEATURES, 'Configuration error: found both hydration and enabledBlocking initial navigation ' + 'in the same application, which is a contradiction.', ); console.warn(message); } }, multi: true, }, ]; } /** * Sets up providers necessary to enable hydration functionality for the application. * * By default, the function enables the recommended set of features for the optimal * performance for most of the applications. It includes the following features: * * * Reconciling DOM hydration. Learn more about it [here](guide/hydration). * * [`HttpClient`](api/common/http/HttpClient) response caching while running on the server and * transferring this cache to the client to avoid extra HTTP requests. Learn more about data caching * [here](guide/ssr#caching-data-when-using-httpclient). * Incremental hydration. [Learn more](guide/incremental-hydration). * * These functions allow you to disable some of the default features or enable new ones: * * * {@link withNoHttpTransferCache} to disable HTTP transfer cache * * {@link withHttpTransferCacheOptions} to configure some HTTP transfer cache options * * {@link withI18nSupport} to enable hydration support for i18n blocks * * {@link withEventReplay} to enable support for replaying user events * * {@link withNoIncrementalHydration} to disable incremental hydration * * @usageNotes * * Basic example of how you can enable hydration in your application when * `bootstrapApplication` function is used: * ```ts * bootstrapApplication(App, { * providers: [provideClientHydration()] * }); * ``` * * Alternatively if you are using NgModules, you would add `provideClientHydration` * to your root app module's provider list. * ```ts * @NgModule({ * declarations: [RootCmp], * bootstrap: [RootCmp], * providers: [provideClientHydration()], * }) * export class AppModule {} * ``` * * @see {@link withNoHttpTransferCache} * @see {@link withHttpTransferCacheOptions} * @see {@link withI18nSupport} * @see {@link withEventReplay} * @see {@link withNoIncrementalHydration} * * @param features Optional features to configure additional hydration behaviors. * @returns A set of providers to enable hydration. * * @publicApi 17.0 */ export function provideClientHydration( ...features: HydrationFeature<HydrationFeatureKind>[] ): EnvironmentProviders { const providers: Provider[] = []; const featuresKind = new Set<HydrationFeatureKind>(); for (const {ɵproviders, ɵkind} of features) { featuresKind.add(ɵkind); if (ɵproviders.length) { providers.push(ɵproviders); } } const hasHttpTransferCacheOptions = featuresKind.has( HydrationFeatureKind.HttpTransferCacheOptions, ); if (typeof ngDevMode !== 'undefined' && ngDevMode) { if (featuresKind.has(HydrationFeatureKind.NoHttpTransferCache) && hasHttpTransferCacheOptions) { throw new RuntimeError( RuntimeErrorCode.HYDRATION_CONFLICTING_FEATURES, 'Configuration error: found both withHttpTransferCacheOptions() and withNoHttpTransferCache() in the same call to provideClientHydration(), which is a contradiction.', ); } if ( featuresKind.has(HydrationFeatureKind.IncrementalHydration) && featuresKind.has(HydrationFeatureKind.NoIncrementalHydration) ) { throw new RuntimeError( RuntimeErrorCode.HYDRATION_CONFLICTING_FEATURES, 'Configuration error: found both withIncrementalHydration() and withNoIncrementalHydration() in the same call to provideClientHydration(), which is a contradiction.', ); } } return makeEnvironmentProviders([ typeof ngDevMode !== 'undefined' && ngDevMode ? provideEnabledBlockingInitialNavigationDetector() : [], typeof ngDevMode !== 'undefined' && ngDevMode ? provideStabilityDebugging() : [], withDomHydration(), featuresKind.has(HydrationFeatureKind.NoHttpTransferCache) || hasHttpTransferCacheOptions ? [] : ɵwithHttpTransferCache({}), featuresKind.has(HydrationFeatureKind.NoIncrementalHydration) ? [] : ɵwithIncrementalHydration(), providers, { provide: CACHE_ACTIVE, useValue: {isActive: true}, }, { provide: APP_BOOTSTRAP_LISTENER, multi: true, useFactory: () => { const appRef = inject(ApplicationRef); const cacheState = inject(CACHE_ACTIVE); return () => { appRef.whenStable().then(() => { cacheState.isActive = false; }); }; }, }, ]); }