dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

Issue adding static server-side rendering (SSR) pages to a globally interactive Blazor Web App #57329

Open ysmoradi opened 1 month ago

ysmoradi commented 1 month ago

Is there an existing issue for this?

Describe the bug

Adding a static server-side rendering (SSR) page to a globally interactive Blazor Web App causes unnecessary page reloads, additional XHR requests, and leads to a poor user experience.

Expected Behavior

I have a globally interactive Blazor Web App (Blazor WASM without pre-rendering). To improve performance on certain pages, I’ve added static SSR-enabled pages using the ExcludeFromInteractiveRouting attribute. To enhance the user experience, I created an empty component called WasmStarter in the client project. In App.razor, when the global render mode of <Routes /> and <HeadOutlet /> is null because HttpContext.AcceptsInteractiveRouting() is false, I included WasmStarter with Blazor WASM render mode. This allows Blazor WASM to download and start in the background while users interact with the static SSR-enabled page content! ❤️

This approach works, but with these two issues:

1- When navigating from a static SSR-enabled page to a Blazor WASM page, Blazor sends an unnecessary XHR request to fetch that page(!). While I'm okay with re-rendering the entire DOM and losing state, the extra XHR request seems irrelevant.

2- When navigating back from a Blazor WASM page to a static SSR-enabled page, the page is completely refreshed and reloaded from the server, which disrupts the user experience. However, navigation between static SSR-enabled pages works smoothly with a simple fetch request, thanks to the Enhanced Navigation feature. I'd expect enhanced navigation works while navigating from blazor wasm enabled page to static ssr enabled page as well.

These issues result in a poor user experience. If this scenario is improved, it could significantly enhance the development of Blazor WASM-powered websites by allowing the most-visited pages to be static SSR, with Blazor WASM seamlessly loading in the background to support the rest of the site.

Steps To Reproduce

1- Create project with dotnet new blazor --framework net9.0 --interactivity WebAssembly --all-interactive --name BlazorSample 2- Move Weather.razor from BlazorSample.Client to BlazorSample project (See changes at once in this git commit) 3- Add ExcludeFromInteractiveRouting attribute to Weather.razor 4- Add Empty component to BlazorSample.Client called WasmStarter 5- Cascade inject HttpContext in App.razor 5- Change

<HeadOutlet @rendermode="InteractiveWebAssembly" />

to

<HeadOutlet @rendermode="(HttpContext.AcceptsInteractiveRouting() is false ? null : new InteractiveWebAssemblyRenderMode(prerender:false))" />

6- Change

<Routes @rendermode="InteractiveWebAssembly" />

to

<Routes @rendermode="(HttpContext.AcceptsInteractiveRouting() is false ? null : new InteractiveWebAssemblyRenderMode(prerender:false))" />

7- Add WasmStarter to App.razor

<BlazorSample.Client.Components.WasmStarter @rendermode="new InteractiveWebAssemblyRenderMode(prerender:false)" />

Exceptions (if any)

No response

.NET Version

9.0.100-preview.7.24407.12

Anything else?

No response

javiercn commented 1 month ago

@ysmoradi thanks for contacting us.

Enhanced navigation doesn't have knowledge of what pages are InteractiveServer/InteractiveWebassembly, etc. and it can't know what the server is going to render without rendering it.

If you navigate from an SSR page to a webassembly page, the browser needs to issue a request to the server as it doesn't know the page runs on webassembly nor where on the page it should render interactive components. Issuing a request to the server is how it knows.

2- When navigating back from a Blazor WASM page to a static SSR-enabled page, the page is completely refreshed and reloaded from the server, which disrupts the user experience. However, navigation between static SSR-enabled pages works smoothly with a simple fetch request, thanks to the Enhanced Navigation feature. I'd expect enhanced navigation works while navigating from blazor wasm enabled page to static ssr enabled page as well.

For this, are you saying that a full page reload happens instead of an enhanced navigation request? If that's so, it's likely by design, enhanced navigation and client-side navigation do not collaborate to handle navigations across the page.

ysmoradi commented 4 weeks ago

For this, are you saying that a full page reload happens instead of an enhanced navigation request? If that's so, it's likely by design, enhanced navigation and client-side navigation do not collaborate to handle navigations across the page.

When the user navigates from a Blazor WebAssembly powered page to a static SSR powered page, you could fetch (Xhr) document from the server, replacing the DOM and throw the current state away instead of reloading the whole browser session. The main issue arises with the performance of Blazor WebAssembly Startup time. Even a small 3 MB Blazor WASM app can take 3 to 5 seconds to initialize (I'm not talking about download time) on low-end mobile devices. When the page is reloaded, and the user navigates back to a Blazor WASM page, they have to endure this initialization time again!

I understand that maintaining state and preserving the DOM across such transitions is challenging. I strongly recommend avoiding a full page reload during navigation. This significantly impacts user experience, especially on mobile devices. Simply fetch the document and throw state away, that's okay, but don't reload the website.