diff --git a/aspnetcore/blazor/components/cascading-values-and-parameters.md b/aspnetcore/blazor/components/cascading-values-and-parameters.md index e3da9e9a7cde..6eb53c000552 100644 --- a/aspnetcore/blazor/components/cascading-values-and-parameters.md +++ b/aspnetcore/blazor/components/cascading-values-and-parameters.md @@ -271,7 +271,7 @@ Cascading parameters don't pass data across render mode boundaries: * State crossing the boundary between static and interactive rendering must be serializable. Components are arbitrary objects that reference a vast chain of other objects, including the renderer, the DI container, and every DI service instance. You must explicitly cause state to be serialized from static SSR to make it available in subsequent interactively-rendered components. Two approaches are adopted: * Via the Blazor framework, parameters passed across a static SSR to interactive rendering boundary are serialized automatically if they're JSON-serializable, or an error is thrown. - * State stored in [`PersistentComponentState`](xref:blazor/components/prerendering-and-integration#persist-prerendered-state) is serialized and recovered automatically if it's JSON-serializable, or an error is thrown. + * State stored in [`PersistentComponentState`](xref:blazor/components/prerender#persist-prerendered-state) is serialized and recovered automatically if it's JSON-serializable, or an error is thrown. Cascading parameters aren't JSON-serialize because the typical usage patterns for cascading parameters are somewhat like DI services. There are often platform-specific variants of cascading parameters, so it would be unhelpful to developers if the framework stopped developers from having server-interactive-specific versions or WebAssembly-specific versions. Also, many cascading parameter values in general aren't serializable, so it would be impractical to update existing apps if you had to stop using all nonserializable cascading parameter values. diff --git a/aspnetcore/blazor/components/prerender.md b/aspnetcore/blazor/components/prerender.md index 022e64ddf9fb..edf318dbacdb 100644 --- a/aspnetcore/blazor/components/prerender.md +++ b/aspnetcore/blazor/components/prerender.md @@ -37,7 +37,10 @@ Consider the following `PrerenderedCounter1` counter component. The component se :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/PrerenderedCounter1.razor"::: -Run the app and inspect logging from the component. The following is example output: +Run the app and inspect logging from the component. The following is example output. + +> [!NOTE] +> If the app adopts interactive (enhanced) routing and the page is reached via an internal navigation, prerendering doesn't occur. Therefore, you must perform a full page reload for the `PrerenderedCounter1` component to see the following output. > :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter1[0]"::: > :::no-loc text=" currentCount set to 41"::: @@ -50,9 +53,6 @@ To retain the initial value of the counter during prerendering, Blazor supports To preserve prerendered state, decide what state to persist using the service. registers a callback to persist the component state before the app is paused. The state is retrieved when the app resumes. -> [!IMPORTANT] -> Persisting component state only works during the initial render of a component and not across enhanced page navigation events. Currently, the service isn't aware of enhanced navigation, and there's no mechanism to deliver state updates to components that are already running. A mechanism to deliver state updates for enhanced navigation is planned for .NET 9, which is targeted for release in late 2024. For more information, see [[Blazor] Support persistent component state across enhanced page navigations (dotnet/aspnetcore #51584)](https://github.com/dotnet/aspnetcore/issues/51584). For more information on enhanced navigation, see . - The following example demonstrates the general pattern: * The `{TYPE}` placeholder represents the type of data to persist. @@ -104,7 +104,10 @@ The following counter component example persists counter state during prerenderi :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/PrerenderedCounter2.razor"::: -When the component executes, `currentCount` is only set once during prerendering. The value is restored when the component is rerendered. The following is example output: +When the component executes, `currentCount` is only set once during prerendering. The value is restored when the component is rerendered. The following is example output. + +> [!NOTE] +> If the app adopts interactive routing and the page is reached via an internal navigation, prerendering doesn't occur. Therefore, you must perform a full page reload for the `PrerenderedCounter2` component to see the following output. > :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter2[0]"::: > :::no-loc text=" currentCount set to 96"::: @@ -113,6 +116,8 @@ When the component executes, `currentCount` is only set once during prerendering By initializing components with the same state used during prerendering, any expensive initialization steps are only executed once. The rendered UI also matches the prerendered UI, so no flicker occurs in the browser. +## Components embedded into pages and views (Razor Pages/MVC) + For components embedded into a page or view of a Razor Pages or MVC app, you must add the [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) with the `` HTML tag inside the closing `` tag of the app's layout. **This is only required for Razor Pages and MVC apps.** For more information, see . `Pages/Shared/_Layout.cshtml`: @@ -125,6 +130,12 @@ For components embedded into a page or view of a Razor Pages or MVC app, you mus ``` +## Interactive routing and prerendering + +Internal navigation for interactive routing doesn't involve requesting new page content from the server. Therefore, prerendering doesn't occur for internal page requests. + +The service only works on the initial page load and not across [enhanced page navigation events](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling). If the app performs a full (non-enhanced) navigation to a page utilizing persistent component state, the persisted state is made available for the app to use when it becomes interactive. But if an interactive circuit has already been established and an enhanced navigation is performed to a page that renders persisted component state, that state isn't made available in the existing circuit. The service isn't aware of enhanced navigation, and there's no mechanism to deliver state updates to components that are already running. + ## Prerendering guidance Prerendering guidance is organized in the Blazor documentation by subject matter. The following links cover all of the prerendering guidance throughout the documentation set by subject: diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index a5580f132eac..fdc3e4563f12 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -31,6 +31,8 @@ When an interactive render mode is assigned to the `Routes` component, the Blazo Static routers use endpoint routing and the HTTP request path to determine which component to render. When the router becomes interactive, it uses the document's URL (the URL in the browser's address bar) to determine which component to render. This means that the interactive router can dynamically change which component is rendered if the document's URL dynamically changes to another valid internal URL, and it can do so without performing an HTTP request to fetch new page content. +Interactive routing also prevents prerendering because new page content isn't requested from the server with a normal page request. For more information, see . + :::moniker-end ## Route templates @@ -179,6 +181,8 @@ app.MapRazorComponents() An interactive render mode can be assigned to the `Routes` component (`Routes.razor`) that makes the Blazor router become interactive after static SSR and static routing on the server. For example, `` assigns interactive server-side rendering (interactive SSR) to the `Routes` component. The `Router` component inherits interactive server-side rendering (interactive SSR) from the `Routes` component. The router becomes interactive after static routing on the server. +Internal navigation for interactive routing doesn't involve requesting new page content from the server. Therefore, prerendering doesn't occur for internal page requests. For more information, see . + If the `Routes` component is defined in the server project, the parameter of the `Router` component should include the `.Client` project's assembly. This allows the router to work correctly when rendered interactively. In the following example, the `Routes` component is in the server project, and the `_Imports.razor` file of the `BlazorSample.Client` project indicates the assembly to search for routable components: diff --git a/aspnetcore/blazor/includes/prerendering.md b/aspnetcore/blazor/includes/prerendering.md index d840bf3ed373..4e6bc22d3604 100644 --- a/aspnetcore/blazor/includes/prerendering.md +++ b/aspnetcore/blazor/includes/prerendering.md @@ -2,6 +2,9 @@ *This section applies to server-side apps that prerender Razor components. Prerendering is covered in .* +> [!NOTE] +> Internal navigation for interactive routing in Blazor Web Apps doesn't involve requesting new page content from the server. Therefore, prerendering doesn't occur for internal page requests. If the app adopts interactive routing, perform a full page reload for component examples that demonstrate prerendering behavior. For more information, see . + :::moniker-end :::moniker range="< aspnetcore-8.0" @@ -30,7 +33,7 @@ The [`OnAfterRender{Async}` lifecycle event](xref:blazor/components/lifecycle#af `PrerenderedInterop1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/prerendering/PrerenderedInterop1.razor" highlight="2-3,10-17"::: +:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/PrerenderedInterop1.razor"::: > [!NOTE] > The preceding example pollutes the client with global functions. For a better approach in production apps, see [JavaScript isolation in JavaScript modules](xref:blazor/js-interop/call-javascript-from-dotnet#javascript-isolation-in-javascript-modules). @@ -66,7 +69,7 @@ Where [!NOTE] > The preceding example pollutes the client with global functions. For a better approach in production apps, see [JavaScript isolation in JavaScript modules](xref:blazor/js-interop/call-javascript-from-dotnet#javascript-isolation-in-javascript-modules).