Web assembly and Blazor: state of the art

I had a first look to Blazor and, more in general, to the Web assembly technologies in 2017. The same year, I’ve written about this topic in the following blog post: Web assembly in .NET Core. After two years, Blazor is near to its first official release, it is no longer experimental, and it is becoming part of the .NET ecosystem. The following article gives some quick updates on Blazor framework.

How Blazor works?

First of all, let’s have a look what’s behind Blazor and how it works using the new Web assembly. The following schema shows the foundations of Blazor:

web assembly blazor

Web assembly stands at the base of the pyramid and it defines a binary standard format which allows running byte code into the browser. Furthermore, one of the

Web assembly is a standard not chained to the .NET ecosystem, but it has been the first step to bring .NET into the client side development.

The other core actor behind Blazor is the Mono framework. Mono is a .NET runtime, and it is part of the .NET runtimes maintained by Microsoft and the community. Mono is designed for portability, therefore it has been compiled into web assembly starting with the following PR: https://github.com/mono/mono/pull/5924

Finally, the top layer there is Blazor. Blazor is the UI framework that defines the startup process of the UI, and also it implements the infrastructure that allows components to communicate together. Starting from .NET Core 3.0, Blazor will be shipped as a part of the framework.

Overview of a Blazor app

It is possible to create a new Blazor template using the following instructions:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview5-19227-01
dotnet new blazor -n <web_app_name>

The first command installs the Blazor template pack using the version 3.0.0-preview5-199227-01 of .NET Core. The second command creates a new base project in the current folder with the web_app_name.

The resulting project and file system will be similar to this:

There are some key parts to notice in the project structure. First of all, the Program and the Startup classes: the first one has the following implementation:

As you can see the above-mentioned snippet uses the BlazorWebAssemblyHost class to initialize a new host using the Startup class. This approach works very similar manner to the approach used in ASP.NET Core applications, but instead of returning an IWebHost type it returns a new instance of the IWebAssemblyHostBuilder interface.

The following code acts using the following namespace Microsoft.AspNetCore.Blazor.Hosting and resolves the Startup class using the following code.

Let’s proceed by having a look at the Startup class which is decidedly simpler compared with the Startup class of an ASP.NET Core application:

The Configure method resolves an instance of the IComponentsApplicationBuilder interface, and it invokes the AddComponent method in order to initialize the App component.

The AddComponent accepts a generic type which represents the main component, and a DOM selector which corresponds to the tag that is used in the index.html page to render the component.

Component-centric structure

Blazor, just like a common UI framework, has a component-centric structure. Components are all the UI elements that compose the pages. In the same way, components can be nested and reused in other parts of the UI.

Every file with the .razor extension is a component. Components render the HTML elements but also can contain UI logic and event handling, for example, let’s have a look to the FetchData.razor file:

The following component fetches some weather forecast data present in the application using an AJAX request, and it renders data in form of a table. As a first step, the component uses the @inject directive to declare a new HTTP client. Secondly, it declares some HTML elements to render in the page, e.g.: the table which contains the forecast data, and it finally declares the UI logic:

The code mentioned above defines a WeatherForecast type and an array which will contain the information fetched from the data, secondly, it declares an override async Task OnInitAsync() function that uses the HttpClient injected in the component to perform an HTTP call to our data. The OnInitAsync function is one of the built-in lifecycle methods implemented by default in the base class of the component.

Built-in lifecycle methods

The following table describes the lifecycle methods which are part of the ComponentBase.cs, and can be customized by the extended classes:

Lifecycle methodsDescription
OnInit /OnInitAsyncThe method executes code at the initialization step of the component.
OnParametersSet /OnParametersSetAsyncThese two methods called when a component has received parameters from its parent caller and the values are assigned to properties. These methods are executed every time the component is rendered.
OnAfterRender/OnAfterRenderAsyncThese methods are called after a component has finished rendering. The elements and the components references are populated at this point.
SetParametersThe method can set a custom code that interprets the incoming parameters value in any way required

Routing

Another essential aspect to notice form the above-described component is the @page "/fetchdata" directive. This directive is part of the routing mechanism of Blazor. By using the same approach of the routing of ASP.NET Core, it is also possible to add custom parameters in the @page value: something similar to @page "/fetchdata/{day}".

Client-side vs Server-side Hosting model

Blazor provides two different hosting models: the client-side one and the server-side.

The client-side hosting model downloads all the .NET dependencies on the client, therefore it doesn’t have any server-side dependency. It provides full web assembly support and also supports offline scenarios. It is possible to create a client-side Blazor app using the following command:

dotnet new blazor -n <webapp_name>

The server-side hosting model is more light-way in terms of resources download on the client. It uses SignalR and web socket technologies to create a communication channel between the client and the server. Therefore, the code runs on the server; the client sends messages at each operation. It also supports old browsers, but it doesn’t have offline support. It is possible to create a server-side Balzor app using the following command:

dotnet new blazorserverside -n <webapp_name>

The main concrete characteristic of between the client-side and server-side hosting models resides in the Program.Main method. The following is the snippet related to a client-side app:

This one is related to a server-side app:

As you can see, the first one returns a reference to the IWebAssemblyHost instance, the second one to an IHostBuilder instance.

Plus, in case of a server-side app, the Startup class also adds a service to the IServiceProvider collection using the services.AddServerSideBlazor() :

The resulting execution of the two hosting models behaves in two different ways. In the case of the client-side approach we can see the following resulting network behavior:

web assembly blazor
Client-side network behavior

The client-side app downloads the blazor.webassembly.js file the mono.wasm file, which is the Mono framework compiled for the web assembly, and it downloads all the .NET dll used by the application: System.dll, System.Core.dll, System.Net.Http.dll …;

On the other side, the server-side app uses a web-socket approach. Therefore the payload downloaded with the page is minimum:

web assembly blazor
Server-side network behavior

Each interaction with the page triggers a new message in the web socket channel:

web assembly blazor
Web-socket channel messages

Final thoughts

Starting in 2017, Blazor is becoming a standard citizen of the .NET ecosystem. Both Microsoft .NET team and the community are investing a lot of time in this project. You can find 3rd party libraries and other material about Blazor here: https://github.com/AdrienTorris/awesome-blazor#libraries–extensions.

Cover image by Corrado Zeni