Blazor is an exciting new web framework from the ASP.NET team that uses Razor, WebAssembly, and Mono to enable the use of .NET on the client. There’s been a lot of excitement about the possibilities this presents, but there’s also been just as much confusion about how these various parts fit together. In this post I’ll attempt to clarify things and show you exactly what each of these technologies do and how they work together to enable .NET in your browser.
- Compiler - Transforms the syntax tree into bytecode, which is a low-level representation of the code that the interpreter can quickly understand and evaluate.
- JIT - A just-in-time interpreter that takes the bytecode and evaluates it on the fly at runtime, thus executing your code.
How WebAssembly Works
WebAssembly is described by the official site as:
"WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine”
Recall that I mentioned Mono at the beginning of this post. It’s arguably the most important part of the .NET-in-the-browser story but it’s probably also the least understood.
In order to evaluate .NET assemblies in a web browser, we need something that's been compiled for WebAssembly that knows what to do with .NET assemblies and IL. In other words, we need a .NET runtime that's been compiled to WebAssembly. When Blazor was first starting out, Steve Sanderson found that he could compile a small, portable, open source .NET runtime called DotNetAnywhere to WebAssembly without too much trouble:
Blazor runs .NET code in the browser via a small, portable .NET runtime called DotNetAnywhere (DNA) compiled to WebAssembly
Unfortunately that didn't scale very well. Thankfully for us, Microsoft already owns an open source, cross-platform, highly-portable .NET runtime. No, not .NET Core. I'm talking about the other open source cross-platform .NET runtime: Mono. Even better, the Mono team had recently accounced they were working on getting Mono to compile to WebAssembly.
While the Mono team continues to address bugs and corner cases, the runtime already works very well on WebAssembly. One important point is that this still has nothing to do with Blazor (other than maybe some incentive). The Mono WebAssembly runtime is totally independent of Blazor and can be used by anyone to evaluate .NET assemblies in the browser. In fact, other projects like Ooui have already started to leverage it.
It's also important to note that this is a full .NET runtime that evaluates .NET assemblies. Unlike the WebAssembly support that compiled languages like C++ and Rust are exploring where the application itself is compiled to WebAssembly, the Mono bits are the only thing that needs to be compiled to WebAssembly. Your own .NET assembly will "just work" when it's loaded and interpreted by the Mono runtime. All that said, the Mono team is also exploring a precompilation scenario for enhanced performance. In that mode, you would essentially compile your .NET code along with the Mono runtime directly into WebAssembly bytecode.
All of this sets up the exciting work going on in Blazor itself. Blazor is the name of a project that includes both a runtime component and various tooling. The tooling helps produce the assemblies that the runtime bits know how to work with. What gets delivered to your browser looks like this:
There's a lot going on here so let's examine each part:
- mono.wasm - This is the actual Mono WebAssembly .NET runtime that
mono.jsloads into the browser. It is basically Mono compiled to WebAssembly.
- mscorlib.dll, etc. - The core .NET assemblies. These need to be loaded just like any other .NET runtime otherwise you'll be missing key parts of the .NET
- myapp.dll - Your Blazor application which was processed by the Razor engine and then compiled by the Blazor tooling. Today the tooling exists as MSBuild tasks that get added to your project by the Blazor NuGet package.
The end result is Razor and C# in your browser! To learn more about Blazor from a developer perspective, check out https://learn-blazor.com/.