Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prerendering and interactive/enhanced routing #31353

Merged
merged 5 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions aspnetcore/blazor/components/prerender.md
Original file line number Diff line number Diff line change
Expand Up @@ -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":::
Expand All @@ -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 <xref:Microsoft.AspNetCore.Components.PersistentComponentState> service. <xref:Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnPersisting%2A?displayProperty=nameWithType> 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 <xref:Microsoft.AspNetCore.Components.PersistentComponentState> 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 <xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling>.

The following example demonstrates the general pattern:

* The `{TYPE}` placeholder represents the type of data to persist.
Expand Down Expand Up @@ -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 (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 `PrerenderedCounter2` component to see the following output.
guardrex marked this conversation as resolved.
Show resolved Hide resolved

> :::no-loc text="info: BlazorSample.Components.Pages.PrerenderedCounter2[0]":::
> :::no-loc text=" currentCount set to 96":::
Expand All @@ -125,6 +128,14 @@ For components embedded into a page or view of a Razor Pages or MVC app, you mus
</body>
```

## 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 <xref:Microsoft.AspNetCore.Components.PersistentComponentState> service only works on the initial page load and not across enhanced page navigation events. 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 <xref:Microsoft.AspNetCore.Components.PersistentComponentState> service isn't aware of enhanced navigation, and there's no mechanism to deliver state updates to components that are already running.
guardrex marked this conversation as resolved.
Show resolved Hide resolved

A mechanism to deliver state updates for enhanced navigation is under consideration 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 <xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling>.
guardrex marked this conversation as resolved.
Show resolved Hide resolved
guardrex marked this conversation as resolved.
Show resolved Hide resolved
guardrex marked this conversation as resolved.
Show resolved Hide resolved

## 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:
Expand Down
4 changes: 3 additions & 1 deletion aspnetcore/blazor/fundamentals/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ If [prerendering isn't disabled](xref:blazor/components/render-modes#prerenderin

When an interactive render mode is assigned to the `Routes` component, the Blazor router becomes interactive after static SSR with static routing on the server. This type of routing is called *interactive routing*.

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.
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.

:::moniker-end

Expand Down Expand Up @@ -179,6 +179,8 @@ app.MapRazorComponents<App>()

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, `<Routes @rendermode="InteractiveServer" />` 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 <xref:blazor/components/prerender#interactive-routing-and-prerendering>.

If the `Routes` component is defined in the server project, the <xref:Microsoft.AspNetCore.Components.Routing.Router.AdditionalAssemblies> 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:
Expand Down
7 changes: 5 additions & 2 deletions aspnetcore/blazor/includes/prerendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

*This section applies to server-side apps that prerender Razor components. Prerendering is covered in <xref:blazor/components/prerender>.*

> [!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 <xref:blazor/components/prerender#interactive-routing-and-prerendering>.

:::moniker-end

:::moniker range="< aspnetcore-8.0"
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -66,7 +69,7 @@ Where <xref:Microsoft.JSInterop.JSRuntime.InvokeAsync%2A?displayProperty=nameWit

`PrerenderedInterop2.razor`:

:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/prerendering/PrerenderedInterop2.razor" highlight="3-4,18,23-29":::
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/PrerenderedInterop2.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).
Expand Down