Kit Wai Chan - Fotolia
Developers, 'Blazor' a new path to web app creation
Expert and Blazor blogger Chris Sainty explains how developers can use a free, open source framework, still under development at Microsoft, to create web applications.
Blazor WebAssembly is the principal hosting model for Blazor applications. Choosing this option means your application runs entirely inside the client's browser, making it a direct alternative to JavaScript SPA (single-page application) frameworks. To understand how this hosting model works we're going to walk through the process of initializing a Blazor WebAssembly application shown in Figure 1.
The process begins when a request is made by the browser to the web server. The web server returns a set of files needed to load the application; these include the host page for the application, usually called index.html, any static assets required by the application such as images, CSS and JavaScript, as well as a special JavaScript file called blazor.webassembly.js.
At this point, you may be wondering why we have a JavaScript file. One of the big selling points of Blazor is the ability to write UI logic using C# instead of JavaScript, right? Yes, this is true, but as of right now, WebAssembly has a fairly large limitation -- it can't alter the Document Object Model (DOM) or call Web APIs directly. These features are planned and being worked on for the next phase of WebAssembly, but until they land, JavaScript is the only way to perform these tasks.
In order to manage this current limitation, part of the Blazor framework resides in JavaScript and it's contained in the blazor.webassembly.js file. This part of the framework does three main things:
- It loads and initializes the Blazor application in the browser.
- It provides direct DOM manipulation, allowing Blazor to perform UI updates.
- It provides APIs for JavaScript interop scenarios.
It's possible that in the future this file will no longer be required, but this depends on how fast features are added to WebAssembly and adopted by browsers. For now, it's an essential part of the framework.
Now we've cleared that up, let's get back to our booting Blazor app. I want to point out that the files returned from the server are all static files. They haven't required any server-side compilation or manipulation. This means that they can be hosted on any service that offers static hosting because there's no requirement for a .NET runtime to be present on the server. For the first time, this opens up free hosting options such as GitHub pages to .NET developers (applies to standalone Blazor WebAssembly applications only).
Once the browser has received all the initial files from the web server, it can process them and construct the DOM. Next, blazor.webassembly.js is executed; this performs many actions, but in the context of starting a Blazor WebAssembly app, it downloads a file called blazor.boot.json, which contains an inventory of all of the framework and application files that are required to run the app.
Most of these files are normal .NET assemblies; there's nothing special about them and they could be run on any compatible .NET runtime. Another type of file is downloaded called dotnet.wasm.
The dotnet.wasm file is a complete .NET runtime -- the mono .NET runtime to be exact -- which has been compiled to WebAssembly.
At this point in time, only the .NET runtime is compiled to WebAssembly; the framework and application are standard .NET assemblies. In the future, a feature called AOT (Ahead of Time) compiling will be introduced, which allows developers to compile parts of their applications into WebAssembly. The benefit of this is performance: Any code compiled to WebAssembly is many times more performant than the interpreted approach used today. This comes with a tradeoff, which is size. AOT compiled code is bigger than the standard assemblies, meaning a larger overall download size for the application.
Once the blazor.boot.json file has been downloaded and the files listed in it have been downloaded, it's time for the application to be run. The WebAssembly .NET runtime is initialized, which in turn loads the Blazor framework and finally the application itself. At this point we have a running Blazor application that exists entirely inside the client's browser. Aside from requesting additional data (if applicable), there's no further reliance on the server.
Calculating UI updates
We now understand how a Blazor WebAssembly application boots up, but how do UI updates get calculated? As we did for the initialization process, we're going to follow a scenario to understand how this happens and what Blazor does (Figure 2).
For our scenario, we have a Blazor WebAssembly application with two pages, Home and Counter. Neither of these pages have anything on them except a heading saying either Home or Counter, respectively. The user is on the home page of the application and will click on a link to go to the counter page. We'll follow the process Blazor goes through to update the UI from that of the home page to the counter page.
When the user clicks on the counter link, the navigation event is intercepted by Blazor on the JavaScript side. This event is then passed over to Blazor on the WebAssembly side, and it's processed by Blazor's router component.
The router checks its routing table for any routable components that match the link the user has attempted to navigate to. In our case, it finds a match with the Counter component, and a new instance of that component is created and the relevant lifecycle methods are executed.
Once complete, Blazor works out the minimum number of changes required to update the DOM to match that of the Counter component. When this is complete, those changes are passed back down to the Blazor JavaScript runtime and that applies those changes to the physical DOM. At this point, the UI updates who the user is on the Counter page.
All of this has happened client-side in the user browser. There was no need for a server during any point in this process. It's fair to say that in a real-world application, you would probably make a call out to a server at some point in this process. This usually happens during the execution of the lifecycle methods of the component being navigated to in order to load some initial data for the component, but this depends on the individual application.
Benefits and tradeoffs
Now that we know a bit more about how the Blazor WebAssembly hosting model works, let talk about the benefits and tradeoffs of choosing this model. Let's start with the benefits.
- Applications run on the client. This means there's much less load on the server; you can offload much of the work to the client. This could lead to significant cost savings on server infrastructure and improve the scalability of an application.
- Apps can work in offline scenarios. As the app runs entirely inside the browser, there's no need for a persistent connection to the server, making applications more tolerant to unstable network connections. It's also trivial to enable progressive web application In fact, Blazor WebAssembly has this as an option you can select when creating your application.
- Apps are deployed as static files. As Blazor WebAssembly apps are static files, they can be deployed anywhere static hosting is available. This opens up some options that have never been available to .NET developers historically. Services such as GitHub pages, Netlify, Azure Blob Storage, AWS S3 Buckets and Azure Static Web Sites are all options for hosting standalone Blazor WebAssembly applications.
- Code sharing. Potentially one of the greatest benefits with Blazor WebAssembly isusing C# on the server. You can now use the same C# objects on your client as you use on the server. The days of keeping TypeScript models in sync with their C# equivalent and vice versa, are over.
Nothing is a silver bullet. Let's understand some tradeoffs of this model.
- Payload. The initial download size for a Blazor WebAssembly app can be considered large. The project template weighs in at around 1.8 MB when published. This is largely due to the fact Blazor needs to ship an entire .NET runtime to the client, which comes in at around 600 KB. This is a one-time cost, as the runtime and many of the framework assemblies are cached on the first load, which means subsequent loads can be a small as a few kilobytes.
- Load time. A knock-on effect of the payload size can be load time. If the user is on a poor internet connection, the amount of time required to download the initial files is higher, which delays the start of the application, leaving the user with a loading message of some kind. This can be offset slightly using server-side prerendering, and although this gives the user something more interesting to look at initially, the app still won't be interactive until all files have been downloaded and initialized. Server-side prerendering for Blazor WebAssembly apps also requires an NET Core element on the server, which negates any free hosting options.
- Restricted runtime. This is arguably not a tradeoff as such, but for existing .NET developers who are used to having a relatively free rein over the machine their apps run on, it's something to be aware of. WebAssembly applications run in the same browser sandbox as JavaScript applications. This means, for example, that you won't be allowed to reach out to the user's machine and do things such access the local file system.
To summarize, Blazor WebAssembly is the hosting model to choose if you're looking for a direct replacement for a JavaScript SPA framework such as Angular, React or Vue. Although there are a few tradeoffs to consider, there are some substantial benefits to choosing this model.