4

Will .NET 6 fix Blazor Prerendering?

 3 years ago
source link: https://jonhilton.net/blazor-prerendering-net6/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Will .NET 6 fix Blazor Prerendering?

April 19, 2021 · 6 minute read · Tags: blazor , prerendering

Nobody wants to see Loading… when they visit your site, but that’s what they’ll get if you use Blazor WASM for your site.

Unless, that is, you employ prerendering.

Ways to avoid loading…

Blazor Server apps load near instantly in the browser, but require a permanent connection to a server (hence the name).

This works well for ‘Line Of Business’ apps but less so for web sites which are to be consumed by the general public (where latency and scaling up can become a problem).

Blazor WASM provides a viable alternative, liberating your app from needing a server (except of course for fetching or persisting data).

The Browser Does The Heavy Lifting

Blazor WASM sites are broadly similar to the many web sites out there today which use javascript (and often a javascript library/framework) to perform most of the processing in the browser.

The only requests to a server are to a) load the initial assets required to run the site, and b) retrieve/transmit data (typically stored in a database).

But here we run smack bang into one of the big drawbacks of Blazor WASM; your users have to wait a variable amount of time for the application to ‘load’ before they can interact with it.

This limitation makes it a sub-optimal choice for many use cases. Take for example a blog. It kind of sucks if your users hit a link to read an article you’ve written only to be faced with a loading spinner while .NET downloads to their browser before they can start reading anything at all.

It also makes things like SEO and social media cards (like those previews you see when a link is shared on Twitter) difficult because every request to your site has to navigate this initial download first.

Which brings us to prerendering.

Considering Blazor for your next project?

Try this simple, repeatable process for transforming ideas into features using Blazor's component model.

4 days; 4 emails; enter your email in the box below and I'll send you lesson #1.

I respect your email privacy. No spam, unsubscribe anytime with one click.

 

With .NET 5 you can configure your Blazor WASM application so that static HTML (generated on the server) is returned in response to the first request made, making for a super snappy first load.

This buys you some time; while your user’s are happily looking at the content that’s already appeared in front of them, their browser can silently download the rest of the assets required to make the site ‘interactive’ in the browser.

Sounds great, what’s the catch?

This sounds like the perfect compromise, so why isn’t everyone doing it?

One reason is that server prerendering rules out hosting your site using static hosting providers like Netlify (because you need a server to handle that first request).

But perhaps the biggest limitation with prerendering in .NET 5 is that data is retrieved twice.

When your users access a page on your site which retrieves data, they will see the page as it was initially rendered on the server, then the page/component will be initialized/rendered again in the browser (when the assets have finished downloading).

As part of this process the data is retrieved again, often leading to a ‘flash’ as the screen renders the data for a second time.

.NET 6 promises to address this problem and, if it works as expected, open the doors to prerendering for many more applications.

At the time of writing (and using .NET 6 Preview 3) this works using something called ComponentApplicationState.

Preserving state between renders

.NET 6 enables you to persist data which has been used for the first render (on the server), then retrieve and use this data again when the app is rendered in the browser.

Taking the standard weather data example from the Blazor project template, here’s how it works.

If you’ve set up your Blazor app for prerendering you’ll probably have a _Host.cshtml page in the Server project.

Make sure to add a call to persist the component state using <persist-component-state>.

<body>

<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />

<persist-component-state/>

<script src="_framework/blazor.webassembly.js"></script>
</body>

This needs to be included after the component(s) whose state you wish to persist. In this case we’re prerendering the entire application (App) so it makes sense to include <persist-component-state> directly after the App component is declared.

Next you’ll need to inject ComponentApplicationState into your component (which is FetchData in this case).

FetchData.razor

@inject ComponentApplicationState ApplicationState

Now you can use ApplicationState to store and retrieve data.

The first step is to make sure any data is persisted when the component is prerendered on the server. For that you can hook into the OnPersisting event.

protected override async Task OnInitializedAsync()
{
    ApplicationState.OnPersisting += PersistForecasts;
}

OnPersisting will fire once your component has been loaded and rendered (which in a prerendering scenario will happen on the server as part of the initial prerender).

private Task PersistForecasts()
{
    ApplicationState.PersistAsJson("fetchData", forecasts);
    return Task.CompletedTask;
}

Here we persist the forecasts data, giving it a key of fetchData.

Remember this part of the process happens on the server.

So PersistAsJson will grab the data once your component has been prerendered on the server. You can think of it as a kind of snapshot of your data at this moment in time.

The next step is to actually check for, and use, this data if it’s available. This is the step that will typically occur when the component is rendered for the second time in the browser.

protected override async Task OnInitializedAsync()
{
    ApplicationState.OnPersisting += PersistForecasts;

    if (ApplicationState.TryTakeAsJson("fetchData", out WeatherForecast[] stored))
        forecasts = stored;
    else
        forecasts = await WeatherForecastService.GetForecastAsync();
}

We now look for that persisted fetchData data (from the first render on the server) and use it if it exists.

Otherwise we can continue as normal and retrieve the data (in this case via the WeatherForecastService). In this scenario this would only happen once, as part of the server prerender.

The result appears seamless; when you visit the page you’ll see it come back quickly (because of the prerendering) and likely won’t even notice the second render (in the browser) because nothing on screen will change.

Complete Example

Here’s the final code (omitting the HTML markup) with the key lines which run on the server (during prerendering) highlighted.

@page "/fetchdata"
@using UI.Shared
@inject IWeatherForecastService WeatherForecastService
@inject ComponentApplicationState ApplicationState
@implements IDisposable

<!-- html here -->

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        ApplicationState.OnPersisting += PersistForecasts;

        if (ApplicationState.TryTakeAsJson("fetchData", out WeatherForecast[] stored))
            forecasts = stored;
        else
            forecasts = await WeatherForecastService.GetForecastAsync();
    }

    private Task PersistForecasts()
    {
        ApplicationState.PersistAsJson("fetchData", forecasts);
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        ApplicationState.OnPersisting -= PersistForecasts;
    }

}

In Summary

When .NET 6 lands later this year you’ll be able to use ComponentApplicationState to grab a snapshot of your component’s state during the initial prerender, then use it to hydrate the component when it’s rendered for the second time in the browser.

This avoids retrieving the same data twice and avoids flashes of content during the second render, resulting in a more seamless experience for your users.

Further reading

Build better ASP.NET web applications, faster

I email every week with hints, tips and advice on how to push through all the noise to get your ASP.NET applications built.

Drop your email in the box below and I'll send new articles straight to your inbox.

I respect your email privacy. No spam, unsubscribe anytime with one click.

 

Next up

Persisting your users preferences using Blazor and Local Storage
Now your app supports dark mode, let’s make sure your visitors only have to choose it once (or ideally, not at all)
Dark mode for your web applications (using Blazor and Tailwind CSS)
Eyestrain is a real problem; help your users by adapting your site to their dark mode preferences
Is it possible to render components “dynamically” using Blazor?
Because life’s not complicated enough already!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK