This content originally appeared on Telerik Blogs and was authored by Claudio Bernasconi
Learn different ways to improve the performance of a Blazor WebAssembly application.
The biggest challenge and criticism Blazor WebAssembly faces is its performance.
Before the application is rendered on the client, the WebAssembly bundle, including a .NET runtime, must be downloaded before the page rendering starts.
In this article, I will discuss a few options for improving the performance of a Blazor WebAssembly application. We’ll start with general recommendations and best practices for Blazor development before diving deep into performance optimizations only applicable to Blazor WebAssembly applications.
Tips for Optimized Component Rendering
Before we head into more advanced topics, let’s start by looking at how to improve the rendering performance of Razor components.
Controlling the Re-Rendering Behavior
If we have a component subtree with many sub-components or expensive data (re)loading, we can prevent the parent component from (re)rendering the whole subtree.
What sounds abstract can be better illustrated using an example:
Let’s say we have a dashboard showing information about different areas of the application. We now want to execute an action handled on the dashboard page, such as clicking a button.
The dashboard page will execute the click handler and provide a new set of parameter values to all child components, which could yield all sub-components re-rendering and reloading their data.
There are two approaches we can use to avoid re-rendering a particular subtree in a Razor component:
- Implement child components with primitive, immutable types, such as
int
,bool
,DateTime
,string
or similar. Those types can be evaluated by the framework to detect any changes. If there aren’t any changes, the child component (and its children) won’t be re-rendered. - Implement or override the
ShouldRender
method in the child component. This lifecycle method provides an interface to programmatically decide whether a (child) component should be re-rendered.
This approach is especially recommended when implementing a component that never needs to re-render again after its initial rendering or when using complex data types as parameters, such as custom classes and record types.
The following code example shows a typical implementation of the OnParametersSet
and the ShouldRender
lifecycle methods in a Razor component:
<h3>@Title - @Price.ToString("C")</h3>
@code {
public string Title { get; set; } = "";
public decimal Price { get; set; } = 0m;
private bool _shouldRender = false;
[Parameter]
public CarInfo? Data { get; set; }
protected override void OnParametersSet()
{
if( Data != null)
{
_shouldRender = Data.Price != Price;
Title = Data.Name;
Price = Data.Price;
}
}
protected override bool ShouldRender()
{
return _shouldRender;
}
public record CarInfo(string Name, decimal Price);
}
In this example, we have a component rendering a car in a car dealership application.
We have a _shouldRender
field, which we set to true if we need to re-render the component. The ShouldRender
implementation is fairly simple and returns this _shouldRender
field, which we set inside the OnParametersSet
method.
The OnParametersSet
method first checks the parameter for a null
value and sets the _shouldRender
field to true
if the new price differs from the previously rendered price.
Depending on your application logic, you set the properties to the new parameter values whether you re-render the component.
Component Virtualization
Component Virtualization improves performance when rendering an extensive list of elements, such as a giant grid or table. The goal is to render only the items visible on the screen, plus a few pre-rendered items.
When the user scrolls the page, new items are rendered, and the items that aren’t on the screen anymore are removed from the DOM to save memory and improve the application’s performance.
The following example shows an implementation using the built-in Virtualize
component:
<Virtualize Items="Orders" Context="order">
<div style="display:flex;">
<div style="width: 400px;">@order.Id</div>
<div>$ @order.Value</div>
</div>
</Virtualize>
In this example, we render the items of an Orders
collection property and use the Context
property to make a specific item available to the child components.
All the rendering is taken care of by the built-in Virtualize
component. It tremendously improves the performance of large lists, especially when each item has a complex render tree and is pretty simple to implement.
You can learn more about Component Virtualization in a dedicated article in the Blazor Basics series.
Optimize JavaScript Interop Performance
When using JavaScript interoperability it’s important to understand that with each call, there is some communication overhead.
It’s best practice to limit the number of calls from .NET to JavaScript and vice versa.
It’s better to have a single call and provide a more complex data object to send all the required information in a single call than to have multiple calls, each containing limited data.
Ahead-of-Time (AOT) Compilation
While the previous performance optimizations were also applicable to Blazor Server interactivity, starting with ahead-of-time (AOT) compilation, we’ll look into optimizations exclusively applicable to Blazor WebAssembly applications.
With AOT compilation, we can compile a Blazor application into native WebAssembly code, which can directly be executed by the browser.
Without AOT, the client downloads the WebAssembly bundle, including the .NET runtime, and executes the .NET code using the just-in-time (JIT) compiler.
AOT-compiled applications take longer to download because the binary size is larger compared to a JIT-compiled application. However, AOT provides better runtime performance.
In controlled scenarios where you deploy an application for your internal employees, using AOT-compiler applications can be interesting because it only takes the employee a single time to download the app and experience better performance each time they use the application.
Implementing AOT is out of the scope of this article, read the official documentation for more information about implementing AOT compilation into Blazor WebAssembly applications.
Serve Compressed Files
A Blazor WebAssembly application does not require ASP.NET Core on the server. Instead, we can deploy a Blazor WebAssembly application as a static web application on any web server.
Make sure the files are served using either Brotli (br) or Gzip (gz) compression.
Use the Network tab in the developer tools in your favorite browser and inspect the Content-Encoding
HTTP header.
If your web server isn’t serving the files using compression, follow the instructions on your web server to implement compression for serving your Blazor WebAssembly application.
Do Not Attach the Debugger
Attaching the debugger takes a long time. If you launch without it, the startup should be noticeably faster.
Often, we want to test a feature or work on the CSS styling of the application or implement a new component. In those cases, we don’t need a debugger.
Running the Blazor WebAssembly application without a debugger can considerably improve the startup performance.
Conclusion
While Blazor WebAssembly improves its performance with every new .NET release, we can also improve it by following best practices.
We want to avoid unnecessary re-rendering of Razor components by controlling the rendering behavior using primitive component parameters or overriding the ShouldRender
lifecycle method.
Where applicable, component virtualization helps render large lists and tables with many rows by only rendering the rows visible to the user. This improves rendering performance and saves memory.
When using JavaScript interoperability, consider the overhead caused by each call. We need to avoid calling it too often. Instead, we should optimize the code and provide as much information as possible with a single JavaScript interop call.
Ahead-of-time (AOT) compilation can improve the application’s performance and increase the WebAssembly bundle—it’s a trade-off.
When configuring a web server running a Blazor WebAssembly application, we need to make sure that we use compression to speed up the download of the WebAssembly bundle.
Attaching the debugger can take some time. Consider starting the application without attaching the debugger when you don’t need it.
If you want to learn more about Blazor development, you can watch my free Blazor Crash Course on YouTube. And stay tuned to the Telerik blog for more Blazor Basics.
This content originally appeared on Telerik Blogs and was authored by Claudio Bernasconi
Claudio Bernasconi | Sciencx (2024-12-17T10:36:52+00:00) Blazor Basics: Optimizing the Performance of Blazor WebAssembly Applications. Retrieved from https://www.scien.cx/2024/12/17/blazor-basics-optimizing-the-performance-of-blazor-webassembly-applications/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.