In 2018, 👿 Microsoft released its new framework, Blazor, which tease MVVM capabilities through component-oriented AJAX or WebAssembly application builds. 4 years later (and with many improvements & versions), what is the current state of Blazor?
In this article, I’ll share with you my experiences with Blazor WebAssembly, the pros, the cons, the whatnot.
TL;DR; If you’re not already a fluent ASP.NET developer, I’m not sure that you want to use it. If you are, then maybe Blazor might be an interesting choice.
This article focuses on Blazor WebAssembly specifics, and not on what I think about C#. But I’m gonna write an article about it.
I won’t talk about Blazor Server since I’ve never used it.
Since Blazor WebAssembly is a front-end MVVM framework, I’ll compare it mostly against other pure JavaScript frameworks (like Vue and Angular that I’ve both used, or React).
I don’t have enough experience with other non-JavaScript frameworks, like Flutter.
Also, keep in mind that I’m an Angular aficionado. Thus, I might have some opinions directly based on my personal preferences, highly based on Angular.
Blazor WebAssembly fits between the structural simplicity of React and the opinionated architecture of Angular with class-based components with bound properties. Components are actually Razor components built for in-browser execution.
You can write your code & template in a single .razor
file or move your code-behind (yeah, legacy terms keep coming) to a .razor.cs
file, to decouple logic & view, which I personally like.
The review
The MVVM features: 👍
Blazor relies solely on components. There are no such things as pipes or directives like you would in Angular (and some other MVVM frameworks), and makes the whole view thing quite verbose compared to Angular (which is already verbose). Need a tooltip around this element? Here you go with one more level of nesting.
Since there are no directives, & because C# does not support mixins, composition & reusability are a bit tedious.
Yet, views are easy to write, & being able to write a bit of code in them can be useful.
Component lifecycle is simple & quite easy to extend & override. You can implement the IDisposable
interface to do some cleanup code. Inputs/outputs bindings are easy to declare in classes & use in views. Injecting dependencies in components is easy as pie.
|
|
Unfortunately, SetParameters(Async)
’s ParameterView
does not allow access to previous values for manual change detection. You then have to assign your [Parameter]
to private fields to keep track of previous states, resulting in more boilerplate code.
But all in all, Blazor’s MVVM approach is quite good
CSS Isolation: 👎
It was the first thing I ended up rage-quitting my workday. CSS isolation of Blazor is 💩, like a huge fat ton of 💩 compared to what Vue or Angular can do. And this is partly due to some takes from Blazor (other parts are a more general problem with the whole ASP.NET thing).
Blazor does not create a root DOM element for your component, but it renders the content directly. While this reduces a bit the number of HTML nodes:
- it prevents parent components from styling child’s container
- you must add a useless
div
wrapper node at the root of your component if you want to use deep selection bound within your component - using inheritance with abstract components might end up with
totally broken selectors not applied at all on your component, in which case you have to manually edit your
.csproj
to manually declare style selector attributes.
Combine those drawbacks with unstable hot-reload features that partially work and make regressions every new version, and the assets usual madness & opacity of ASP.NET feat. Visual Studio, and you’ll end up losing hours wondering why your styles aren’t applied and aren’t even in your client .css
. You’ve been warned.
Inheriting ASP.NET, for (the better and) the worse: 👎
Automagical assets random behavior
This section is probably heavily biased by my experience with the JS ecosystem. If you’re an expert ASP.NET developer, I bet you’ll say I’m just a noob.
Yet, I’ve seen soooo few ASP.NET applications built correctly for the client side. Like, un-minified & non-concatenated code in production, non-scaled images, no srcset
, etc. So I also suppose we don’t have the same expectations from the build process of a web application
ASP.NET want to look like a full-fledged web framework, with built-in web server (Kestrel), middlewares, user concept, HTML rendering, serialization, etc…; ok fine. But who do web applications without JS/CSS/images ? So why the f*ck do I have to use 3rd party stuff to do proper assets handling & post-processing ?
Just to be clear: I don’t think that’s the job of ASP.NET nor Visual Studio to do this. But the whole build process configured by declarative XML in .csproj
is very hard to dig deep in & debug. And I don’t mention the magic behind AOT that builds to WASM if tooling is available (but I’ll tell you more about it
later), or just skip without saying anything otherwise.
So, if you plan on having any .scss
in your project, or want to do some minification/concatenation/compression/optimization, you’ll need extra tooling like
Excubo.WebCompiler.
Well, if you give it a go long enough, you’ll experience “funny” behaviors by yourself.
The legacy hell
If you’ve already written any serious project with ASP.NET, you must have encountered obsolete or incoherent things the Microsoft team won’t change, because, you know, Legacy. And backward compatibility is good. But who is going to update their old ASP.NET 3 application to Core without planing a full rewrite of the app ?
There are a lot of APIs that evolves regularly in the ASP.NET ecosystem, but mostly surfacing ones. More critical/central parts, such as authentication, just don’t move. And if you’re trying to write, for example, an app with multi-tenant authentication, you’ll probably hit your head against the walls (even if there are some great tutorials, but if you find one that works, just stop updating anything).
There is much more to say about this issue. I lost several days on multi-tenant app bug but I can’t remember exactly the context nor the resources I used.
Next time I work on a Blazor project, I’ll write it down and update this section.
For god’ sake, the DOCS
Docs in the ASP.NET ecosystem are…. Very inconsistent.
Microsoft’s docs are so abundant for every version that you’ll probably have troubles finding what you are looking for, drowning in examples & multiple versions. But at least, there are some
For NuGets, that’s another story. A good chunk of the NuGet repository packages just don’t even have a readme, nor a public repository attached. I guess that this is in part due to ASP.NET history & target audience: mostly closed-source for a while, and for enterprises that don’t really like exposing code for free. So why bother writing docs if nobody will use it ?
Testing: 👍
I have not been in a context favorable to testing, I admit, and I did it very poorly for most of it. I just wrote a couple of components tests, & fully tested some blocks. And it was quite cool. I was very afraid that the straightness of C# would not be as easy as Typescript to test.
A couple of NuGets filled the gaps perfectly:
-
xUnit: defines general test utilities, like
Fact
&Theory
- bUnit: create & manage components to manipulate them in tests
- Moq: create mocks of your classes & services that you can inject in DI. This one is so useful.
Altogether, a test could look like this:
|
|
WebAssembly is just a name, not a performance guarantee: 😐
Well…. Microsoft doesn’t talk about Blazor’s performance as a selling point. And there’s a reason: it’s poor. Like, deceiving. But that’s not Blazor’s only’s fault.
DOM manipulation takes an absurd amount of time: currently (as of 01/2022), WASM can’t interact with the DOM directly. Thus, every attempt to manipulate it needs to go through the JSInterop
to run a javascript call from Dotnet. And this is painfully slow. For an MVVM framework, being super slow to manipulate your page’s elements is disqualifying.
Apart from the real DOM manipulation, the per-components overhead is so consequent that I found myself merging several components together to make an all-in-one-joker-fatass-huge-template for performance reasons, because rendering 25 rows with single-responsibility instances was causing significant lag (like 5s delay per redraw). And yet again, this kind of defeats the axiom of Single Responsibility Principle, where components should have one and only one job.
Microsoft’s Blazor performances best practices can’t even decide what to recommend: don’t do big components with many DOM elements, but don’t use many small ones either. In brief, the only way to be blazing fast is not doing anything.
Many benchmarks indicate that WASM is faster for CPU-heavy jobs. But most applications don’t need nor have a real advantage to benefit from the performance boost of WASM, knowing WASM modules are heavy. And if you do, here is the truth:
Blazor WebAssembly does not build to WASM by default. Yeah, you read it correctly:
The default configuration builds to
IL and outputs DLLs. So, all the code you write ends up in .dll
files, not in .wasm
. Then, Blazor (the WASM core this time) bootstrap a WASM sandbox to interpret your IL code.
You’re basically outputting huge DLLs interpreted by a sub-optimized IL runtime in WASM, to run an application the WASM interpreter is knowingly shitty at.
I’ve just said above that “Blazor WebAssembly does not build to WASM by default”, but you can opt in to this feature by enabling AOT. But beware: AOT takes very long (more than 10 min for a small app) and increases yet again the total application size by a lot (it basically copies all your client’s DLLs to WASM format which is about the same size as DLLs). If you’re using docker, just be ready to push 300Mo+ images quite rapidly, and pay the storage fees that come with them.
But once compiled to WASM, performances are decent. Rendering speed is still lower than Angular (which is not that fast either depending on the exact task), but acceptable, and processing that don’t use JSInterop
(like most parts of services) are finally quite fast.
Conclusion
Blazor WebAssembly is a thing. Definitely. But it is IMO very immature by itself, and by the main features it tries to leverage, WebAssembly. Yet, it’s a nice try.
The dependency on the ASP.NET ecosystem is a pain point, but using C# can be interesting if you have your business logic already written in this language.
Yet, I’m not sure that a strict language like C# is appropriate for front-end, since the UI usually need models transformations to view-models that might be tight to components, and C#’s structures declaration verbosity is more a constraint than an advantage in this perspective.
I’m not opposed to use Blazor again (and I’ll have to anyway since I already have a production-grade application with it), but on very specific occasions.
Don’t hesitate to share your experiences!