Blazor Framework
Blazor Framework
Blazor Framework
com/dotnet/aspnetcore/issues/21514
Blazor components are .NET classes that represent a reusable piece of UI. Each component maintains its own state
and specifies its own rendering logic, which can include rendering other components. Components specify event
handlers for specific user interactions to update the component's state.
After a component handles an event, Blazor renders the component and keeps track of what changed in the
rendered output. Components don't render directly to the Document Object Model (DOM). They instead render to
an in-memory representation of the DOM called a RenderTree so that Blazor can track the changes. Blazor
compares the newly rendered output with the previous output to calculate a UI diff that it then applies efficiently to
the DOM.
The components render, and the calculated UI diff is serialized and sent to the browser where it's applied to the DOM.
Root component
In the Blazor Server app, the root component’s host page is defined in the *_Host.cshtml* file. This file defines a Razor
Page, not a component. Razor Pages use Razor syntax to define a server-addressable page, very much like an .aspx
page. The Html.RenderComponentAsync<TComponent>(RenderMode) method is used to define where a root-level
component should be rendered.
Loading steps
:::no-loc(Blazor WebAssembly):::
The principal hosting model for :::no-loc(Blazor)::: is running client-side in the browser on WebAssembly. The
:::no-loc(Blazor)::: app, its dependencies, and the .NET runtime are downloaded to the browser. The app is executed
directly on the browser UI thread. UI updates and event handling occur within the same process. The app's assets are
deployed as static files to a web server or service capable of serving static content to clients. Because the app is
created for deployment without a backend ASP.NET Core app, it's called a standalone :::no-loc(Blazor WebAssembly):::
app .
:::no-loc(Blazor Server):::
With the :::no-loc(Blazor Server)::: hosting model, the app is executed on the server from within an ASP.NET Core app.
UI updates, event handling, and JavaScript calls are handled over a :::no-loc(SignalR)::: connection.
Circuits
A :::no-loc(Blazor Server)::: app is built on top of ASP.NET Core :::no-loc(SignalR):::. Each client communicates to the server over
one or more :::no-loc(SignalR)::: connections called a circuit . A circuit is :::no-loc(Blazor):::'s abstraction over :::no-loc(SignalR):::
connections that can tolerate temporary network interruptions. When a :::no-loc(Blazor)::: client sees that the :::no-loc(SignalR):::
connection is disconnected, it attempts to reconnect to the server using a new :::no-loc(SignalR)::: connection.
HTML Generation
Interpreted mode
In interpreted mode, the Mono runtime itself is compiled to WebAssembly, but your .NET assembly files are
not. The browser can then load and execute the Mono runtime, which in turn can load and execute standard
.NET assemblies (regular .NET .dll files) built by the normal .NET compilation toolchain.
This is similar to how, for the desktop CLR, the core internals of the CLR are distributed precompiled to native
code, which then loads and executes .NET assembly files. One key difference is that the desktop CLR uses
just-in-time (JIT) compilation extensively to make execution faster, whereas Mono on WebAssembly is closer
to a pure interpretation model.
In AOT mode, your application’s .NET assemblies are transformed to pure WebAssembly binaries at build
time. At runtime, there’s no interpretation: your code just executes directly as regular WebAssembly code. It’s
still necessary to load part of the Mono runtime (e.g., parts for low-level .NET services like garbage
collection), but not all of it (e.g., parts for parsing .NET assemblies).
This is similar to how the ngen tool has historically allowed AOT compilation of .NET binaries to native
machine code, or more recently, CoreRT provides a complete native AOT .NET runtime.
What we do know is that interpreted mode provides a much faster development cycle than AOT. When you
change your code, you can rebuild it using the normal .NET compiler and have the updated application
running in your browser in seconds. An AOT rebuild, on the other hand, might take minutes.
So one obvious thought is that interpreted mode might be for development, and AOT mode might be for
production.
But that might not turn out to be the case, because interpreted mode is surprisingly much faster than you’d
think, and we hear from Xamarin folks who use .NET for native mobile apps that regular (non-AOT) .NET
assemblies are very small and compression-friendly compared with AOT-compiled assemblies. We’re keeping
our options open until we can measure the differences objectively.
@page "/customer-list"
@using Services
@inject IDataAccess DataRepository
Complex services might require additional services. In the prior example, DataAccess might require Blazor's default
service HttpClient. @inject or the InjectAttribute can't be used in services. Constructor injection must be used instead.
Required services are added by adding parameters to the service's constructor. When dependency injection creates the
service, it recognizes the services it requires in the constructor and provides them accordingly.
The following code sample demonstrates the concept:
AuthorizeView
<AuthorizeView>
<Authorized>
<a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
<a href="Identity/Account/LogOut">Log out</a>
</Authorized>
<NotAuthorized>
<a href="Identity/Account/Register">Register</a>
<a href="Identity/Account/Login">Log in</a>
</NotAuthorized>
</AuthorizeView>
The AuthorizeView component will only display its child content when the user is authorized. Alternatively, the
AuthorizeView takes parameters for specifying different templates when the user is Authorized, NotAuthorized, or
Authorizing
Registering authentication
With ASP.NET Core Identity things are actually straightforward and there’s no reason to discuss it. It’s more interesting
to find out how things are configured in Startup class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseAuthentication();
app.UseAuthorization();
}
<textarea ref="text"></textarea>
@functions {
ElementRef text;
}
In this example we create a reference text for a textarea element. The ElementRef handle is completely opaque for
Blazor, so you cannot do anything with itm except passing as-it-is to a JavaScript function.
@function {
JSRuntime.Current.InvokeAsync<string>( "console.log", text);
}
Components
Each Blazor page is a BlazorComponent, and as such it follows the component lifecycle: first, it receives parameters from its
parent in the render tree. Then, the OnInitAsync method of the Index.cshtml page is invoked. But how do we implement
this method in C# inside the Blazor .cshtml file? We must include our C# code for that page in a @functions directive:
Hide Copy Code
@functions {
protected override async Task OnInitAsync()
{
}
}
JavaScript/TypeScript interop
To call other JS libraries or your own custom JS/TS code from .NET, the current approach is to register a named
function in a JavaScript/TypeScript file, e.g.:
// This is JavaScript
Blazor.registerFunction('doPrompt', message => {
return prompt(message);
});
The registerFunction approach has the benefit of working nicely with JavaScript build tools such as Webpack.
And to save you the trouble of doing this for standard browser APIs, the Mono team is working on a library that exposes
standard browser APIs to .NET.
Component parameters
Components can have component parameters, which are defined using non-public properties on the
component class decorated with [Parameter]. Use attributes to specify arguments for a component in
markup.
ParentComponent.cshtml:
@page "/ParentComponent"
<h1>Parent-child example</h1>
<ChildComponent Title="Panel Title from Parent">
Child content of the child component is supplied by the parent component.
</ChildComponent>
ChildComponent.cshtml:
@functions {
[Parameter]
private string Title { get; set; }
[Parameter]
private RenderFragment ChildContent { get; set; }
}
Child content
Components can set the content in another component. The assigning component provides the content
between the tags that specify the receiving component. For example, a ParentComponentcan provide content
that is to be rendered by a ChildComponent by placing the content inside <ChildComponent> tags.
ParentComponent.cshtml:
@page "/ParentComponent"
<h1>Parent-child example</h1>
The child component has a ChildContent property that represents a RenderFragment. The value of
ChildContent is positioned in the child component's markup where the content should be rendered. In the
following example, the value of ChildContent is received from the parent component and rendered inside the
Bootstrap panel's panel-body.
ChildComponent.cshtml:
<div class="panel panel-success">
<div class="panel-heading">@Title</div>
<div class="panel-body">@ChildContent</div>
</div>
@functions {
[Parameter]
private string Title { get; set; }
[Parameter]
private RenderFragment ChildContent { get; set; }
}
NOTE
The property receiving the RenderFragment content must be named ChildContent by convention.
Lifecycle methods
OnInitAsync and OnInit execute code after the component has been initialized. To perform an asynchronous operation,
use OnInitAsync and use the await keyword:
protected override async Task OnInitAsync()
{
}
For a synchronous operation, use OnInit:
protected override void OnInit()
{
}
OnParametersSetAsync and OnParametersSet are called when a component has received parameters from its parent
and the values are assigned to properties. These methods are executed after OnInit during component initialization.
protected override async Task OnParametersSetAsync()
{
}
protected override void OnParametersSet()
{
}
OnAfterRenderAsync and OnAfterRender are called each time after a component has finished rendering. Element and
component references are populated at this point. Use this stage to perform additional initialization steps using the
rendered content, such as activating third-party JavaScript libraries that operate on the rendered DOM elements.
protected override async Task OnAfterRenderAsync()
{
}
base.SetParameters(parameters);
}
If base.SetParameters isn't invoked, the custom code can interpret the incoming parameters value in any way required.
For example, the incoming parameters aren't required to be assigned to the properties on the class.
ShouldRender can be overridden to suppress refreshing of the UI. If the implementation returns true, the UI is
refreshed. Even if ShouldRender is overridden, the component is always initially rendered.
protected override bool ShouldRender()
{
var renderUI = true;
return renderUI;
}
ShouldRender
You can override ShouldRender to suppress refreshing of the UI. If your implementation returns true, UI is refreshed.
Otherwise, changes are not propagated to the UI. Note that an initial rendering is always performed, independent of the
return value of ShouldRender.
Parent form
Child form
<button onclick=@(() => OnSearch(criteria)) type="button" class="btn btn-danger px-5">
Search ➝
</button>
@functions
{
[Parameter] Func<SearchCriteria, Task> OnSearch { get; set; }
}