📜 ⬆️ ⬇️

Blazor: Technical Introduction

Today , the ASP.NET team announced that the Blazor project has been moved to the ASP.NET organization repository . We are starting the experiment phase to see if we can develop Blazor into a supported product. This is a big step forward!

image

What is Blazor? This is a browser application framework written in .NET and launched using WebAssembly. It gives you all the benefits of rich modern single-page applications (SPA), while allowing you to use .NET from beginning to end, up to the common code on the server and client. In the post with the announcement describes in detail the main cases of application, timing and so on.
')
In this post I want to talk more deeply about the technical details for those who are interested in how it works.

Running .NET in the browser


The first step to build a SPA framework on .NET is to somehow get the ability to run .NET code in a browser. Finally, this can be done using open standards and work in any browser (without any plug-ins), thanks to WebAssembly.

At the moment, WebAssembly is supported by all major browsers, including mobile. This is a compact bytecode format optimized to reduce the amount of data downloaded and speed up execution. Although many developers might think so, WebAssembly does not introduce any new security issues, as these are not ordinary binary files (like x86 / x64) - this is a new format that contains bytecode that can only do the same thing as and javascript.

So how does it allow us to run .NET? This is due to the fact that the Mono team added WebAssembly support to their project . If you missed the news, the Mono project became part of Microsoft in 2016. Mono is the official .NET runtime for client platforms (such as native mobile apps and games). WebAssembly is just another client platform, so it’s reasonable that Mono should work on it.

Mono can run on WebAssembly in two modes: interpretation mode and AOT.

Interpretation


In runtime interpreting mode, Mono is compiled into WebAssembly, but your .NET assemblies are not. The browser downloads and runs the runtime, which in turn can load and execute standard .NET assemblies (regular .NET .dll files) compiled with the usual .NET toolchain.

Diagram showing interpretation mode

This is similar to how for a common CLR, the main core is distributed compiled into native code, which then loads and executes .NET assemblies. The only key difference is that the desktop CLR makes extensive use of JIT compilation to speed up execution, while Mono on WebAssembly works closer to the classical interpretation model.

Ahead-of-time (AOT) compilation


In AOT mode, your .NET application turns into clean WebAssembly binaries immediately when building. At runtime, no interpretation takes place - your code is executed as normal WebAssembly code. This mode still requires loading some of the Mono runtime (such low-level .NET services, such as garbage collection), but it allows you to discard such components as the .NET parser files.

Diagram showing AOT mode

This is similar to how, from time immemorial, the ngen utility allows AOT compilation of .NET assemblies to native machine code, or to the recently appeared full-fledged native AOT .NET runtime - CoreRT .

Interpretation mode against AOT


Which mode is better? We don't know yet.

However, we know that the interpretation mode gives a much faster development process than AOT. After changing the code, you can rebuild it with a standard .NET compiler and get the updated application in a browser in seconds. AOT compilation, in turn, can take minutes.

The obvious thought is that the interpretation mode will be the main one for the development, and the AOT will be for the production.

But all this may not be the case at all, because the interpretation mode, surprisingly, is much faster than you might think. And we heard from the guys from Xamarin, who use .NET for native mobile applications, that regular (not AOT) .NET assemblies are very small and respond well to compression, unlike AOT assemblies. We will consider both options until we have the opportunity to objectively assess the difference.

Blazor, SPA framework


Being able to run .NET in a browser is a good start, but not enough. To be a productive application developer, you need a consistent set of standard solutions for standard problems — such as building / re-using UI, state management, routing, unit testing, build optimization, and so on. All of this should be designed around the strengths of .NET and C #, allowing you to get the most out of your existing .NET ecosystem and come with first-class tool support, as the .NET developer expects.

Blazor is all of the above. It is inspired by today's best SPA frameworks, such as React, Vue and Angular, as well as some Microsoft UI stacks like Razor Pages. Our goal is to give web developers something that blends as well as possible with .NET.

Components


In all modern SPA frameworks, applications are built from components. A component is usually some kind of UI element: a page, a dialog, a set of tabs, or a form. Components can be embedded into each other, reused and divided between projects.

In Blazor, a component is a .NET class that you can write directly (that is, as a C # class) or, more generally, as a Razor markup page (.cshtml file).

Introduced around 2010, Razor is a syntax for combining markup with C # code. It is designed specifically for developer productivity, allowing you to switch between markup and C # without any ceremonies, with full intellisense support. The example below shows a simple dialog component described in the Razor file MyDialog.cshtml:

<div class="my-styles"> <h2>@Title</h2> @RenderContent(Body) <button onclick=@OnOK>OK</button> </div> @functions { public string Title { get; set; } public Content Body { get; set; } public Action OnOK { get; set; } } 

When you use this component, the toolkit knows what to tell you:

Animated GIF showing support for Blazor components in tools

Many design patterns can be built on this simple foundation, including popular patterns from SPA frameworks such as stateful components, functional stateless components, and higher-order components. You can put components into each other, procedurally generate them, share between libraries, run unit tests without the need for a browser and, in general, live a good life.

Infrastructure


When creating a new project, Blazor will offer the basic services needed by most applications:


An important aspect of architecture is all optional. If you do not use something - it will be removed from the final build when publishing.

Another important point is that only a few of the lowest-level parts are located in the core of the framework. For example, the routing and the template system are not so - they are implemented in the “user space”, that is, this code can be written by the application developer without using any internal APIs. Therefore, if you do not like our routing or template system, you can replace them with your own. Our current prototype of the template system is about 30 lines of C # code, so you can easily figure it out and rewrite it if you want.

Deployment


Obviously, a significant portion of the Blazor target audience is ASP.NET developers. For them, we will release middleware for Blazor transparent UI hosting with additional features like pre-rendering on the server.

No less important for us and the developers are not using .NET at all. For Blazor to be viable for developers preferring Node.js, Rails, PHP or any other server technology, or even writing serverless applications, we absolutely will not require the presence of .NET on the server. The result of building a Blazor application is the dist folder, in which only static files are located. You can distribute them from Github pages, from cloud storages, through Node.js server and generally through anything.

Common code and netstandard


The .NET standard is a way to describe the level of capabilities provided by the .NET runtime or required by the .NET assembly. If your .NET runtime supports netstandard2.0 and below, and you have an assembly aimed at netstandard2.0 and above, then you can run this build on this runtime.

Mono on WebAssembly will support netstandard2.0 or higher (depending on release dates). This means that you can use your .NET libraries both on the backend and in browser applications. For example, you may have a project with classes of business logic models - it can be used both on the server and on the client. And, of course, you can download packages from NuGet.

However, not all .NET APIs make sense in the browser. For example, you cannot listen to an arbitrary TCP socket, so System.Net.Sockets.TcpListener will not do anything useful. Also, you almost certainly should not use System.Data.SqlClient in a browser application. And this is not a problem, because, firstly, browsers still support APIs that people really need to create web applications, and secondly, the .NET standard has a processing mechanism for such cases. When calling a class that is not applicable to a specific API, the base class system (BCL) will throw a PlatformNotSupported exception. At the beginning, this can lead to problems, but over time, the authors of NuGet packages will make changes to their libraries to support different platforms. If .NET wants to move in the direction of the most rapidly developing application platform in the world - this is the level to climb.

Javascript / typeScript compatibility


Even if you are writing a browser application in C # / F # - sometimes you need to connect someone else's JavaScript library or your own JavaScript / TypeScript code to call some new browser API.

This should be very simple, as the WebAssembly standard is designed to interact with JavaScript (and this is not surprising) and we can easily use it in .NET code.

To work with foreign JavaScript libraries, we explore the possibility of using TypeScript type definitions in C # code with full intellisense. This will make about 1000 of the most popular JS libraries very easy to integrate.

The current approach to calling other people's libraries or your JS / TS code from .NET is to register the named function in the JS / TS file. For example:

 //  JavaScript Blazor.registerFunction('doPrompt', message => { return prompt(message); }); 

... and then create a wrapper to call from .NET:

 //  C# public static bool DoPrompt(string message) { return RegisteredFunction.Invoke<bool>("doPrompt", message); } 

The registerFunction approach has a nice bonus in the form of good work with JavaScript collectors like the Webpack.

And, to save your time and nerves, the Mono team is working on a library that will forward standard browser APIs to .NET.

Optimization


Historically, .NET has focused on platforms where the size of the application is not such a big problem. It doesn't make much difference whether your ASP.NET application weighs 1MB or 50MB. This is a moderate problem for desktop or mobile applications. But for browsers, download size is very critical.

In defense, we can say that .NET on WebAssembly will most likely be loaded only once. After all, you can use standard HTTP caching (or even fancy things like service worker) to ensure that the user loads the runtime kernel only once. And if you use CDN, then the user can use the results of one download at once in several applications.

All this is good, but I do not think that is enough. If runtime weighs 20MB, then it's still too much, even for a one-time download. This is not a browser plugin, after all - it is a standard, web-based, web application. Even the very first download should be fast.

Therefore, we put a lot of effort into reducing the size of the load. We see the following 3 phases of optimizations:

1. Mono runtime reduction


Mono Runtime contains many desktop-specific features. We hope that Blazor will contain a trimmed version of Mono, which is significantly less than the full distribution. With a manual optimization attempt, I was able to remove about 70% of the .wasm runtime file without disrupting the operation of the base application.

2. Reducing IL code when publishing


The .NET IL linker (based on the Mono linker ) performs static analysis, determining which parts of the .NET libraries can be called from your application, and deletes everything else.

This is similar to tree shaking in JavaScript , the difference being that the IL linker is much more accurate and works at the level of individual methods. This allows you to remove all unused code of the system library, which makes a huge difference in most cases, often reducing the size of the application by another 70 +%.

3. Compression


Finally, the most obvious is that we expect your server to support HTTP compression. This usually cuts off another 75% of the volume.

Of course, a .NET web application will never be as miniature as the simplest React application. Our goal is to make it so small that a typical user on an average connection will not notice even the very first boot, not to mention the subsequent downloads from the cache.

So why is all this necessary?


Whether you like it or not, web development will change a lot in the next few years. WebAssembly allows web developers to choose from a much larger list of languages ​​and platforms than ever. And this is good - our world is finally growing up. The developers of the server software and native applications have always been able to choose the language and paradigms that are best suited to solve their problems, correspond to the culture of the team and are supported by existing knowledge. Do you dream of writing a function in Haskell or Lisp for your financial application? Want some low-level C? Are you an Apple developer and want to continue using your Swift knowledge? All this will come to the web.

Do not be scared. This does not mean that you will need to know all these languages. This means that we all become ordinary software developers. Your current programming knowledge for browsers is still relevant and valuable, but you will have new ways to express your ideas and more points of contact with other developer communities.

And our initiative is to put .NET in the vanguard of this movement, and not to drag behind, behind the years.

Current status


Feel like trying? Slow down - we are still in the very early stages of the project. So far, nothing is ready for download and much of the above in the development process. Most of you should just relax and wait, the first pre-alpha builds will appear in about a month.

Remember, at the moment Blazor is an experiment for the ASP.NET team. It will take us several months to see if we can make a full-fledged, supported product out of it. We still do not promise anything, so do not build your business plans around Blazor!

If you are very interested, then look into the repository , try to build it, run the tests and come and chat with us . You can even search for // TODO comments and send a pull request, or share an idea about a cool feature.


From translator


In conclusion, here are some interesting links:

Source: https://habr.com/ru/post/348660/


All Articles