Skip to content

Commit 960019b

Browse files
authored
Blazor Virtualize component (#19898)
1 parent 3523d47 commit 960019b

3 files changed

Lines changed: 158 additions & 4 deletions

File tree

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
title: ASP.NET Core Blazor component virtualization
3+
author: guardrex
4+
description: Learn how to use component virtualization in ASP.NET Core Blazor apps.
5+
monikerRange: '>= aspnetcore-3.1'
6+
ms.author: riande
7+
ms.custom: mvc
8+
ms.date: 09/16/2020
9+
no-loc: ["ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR]
10+
uid: blazor/components/virtualization
11+
---
12+
# ASP.NET Core Blazor component virtualization
13+
14+
By [Daniel Roth](https://github.com/danroth27)
15+
16+
Improve the perceived performance of component rendering using the Blazor framework's built-in virtualization support. Virtualization is a technique for limiting UI rendering to just the parts that are currently visible. For example, virtualization is helpful when the app must render a long list or a table with many rows and only a subset of items is required to be visible at any given time. Blazor provides the `Virtualize` component that can be used to add virtualization to an app's components.
17+
18+
::: moniker range=">= aspnetcore-5.0"
19+
20+
Without virtualization, a typical list or table-based component might use a C# [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop to render each item in the list or each row in the table:
21+
22+
```razor
23+
<table>
24+
@foreach (var employee in employees)
25+
{
26+
<tr>
27+
<td>@employee.FirstName</td>
28+
<td>@employee.LastName</td>
29+
<td>@employee.JobTitle</td>
30+
</tr>
31+
}
32+
</table>
33+
```
34+
35+
If the list contains thousands of items, then rendering the list may take a long time. The user may experience a noticeable UI lag.
36+
37+
Instead of rendering each item in the list all at one time, replace the [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with the `Virtualize` component and specify a fixed item source with `Items`. Only the items that are currently visible are rendered:
38+
39+
```razor
40+
<table>
41+
<Virtualize Context="employee" Items="@employees">
42+
<tr>
43+
<td>@employee.FirstName</td>
44+
<td>@employee.LastName</td>
45+
<td>@employee.JobTitle</td>
46+
</tr>
47+
</Virtualize>
48+
</table>
49+
```
50+
51+
If not specifying a context to the component with `Context`, use the `context` value (`@context.{PROPERTY}`) in the item content template:
52+
53+
```razor
54+
<table>
55+
<Virtualize Items="@employees">
56+
<tr>
57+
<td>@context.FirstName</td>
58+
<td>@context.LastName</td>
59+
<td>@context.JobTitle</td>
60+
</tr>
61+
</Virtualize>
62+
</table>
63+
```
64+
65+
The `Virtualize` component calculates how many items to render based on the height of the container and the size of the rendered items.
66+
67+
## Item provider delegate
68+
69+
If you don't want to load all of the items into memory, you can specify an items provider delegate method to the component's `ItemsProvider` parameter that asynchronously retrieves the requested items on demand:
70+
71+
```razor
72+
<table>
73+
<Virtualize Context="employee" ItemsProvider="@LoadEmployees">
74+
<tr>
75+
<td>@employee.FirstName</td>
76+
<td>@employee.LastName</td>
77+
<td>@employee.JobTitle</td>
78+
</tr>
79+
</Virtualize>
80+
</table>
81+
```
82+
83+
The items provider receives an `ItemsProviderRequest`, which specifies the required number of items starting at a specific start index. The items provider then retrieves the requested items from a database or other service and returns them as an `ItemsProviderResult<TItem>` along with a count of the total items. The items provider can choose to retrieve the items with each request or cache them so that they're readily available. Don't attempt to use an items provider and assign a collection to `Items` for the same `Virtualize` component.
84+
85+
The following example loads employees from an `EmployeeService`:
86+
87+
```csharp
88+
private async ValueTask<ItemsProviderResult<Employee>> LoadEmployees(
89+
ItemsProviderRequest request)
90+
{
91+
var numEmployees = Math.Min(request.Count, totalEmployees - request.StartIndex);
92+
var employees = await EmployeesService.GetEmployeesAsync(request.StartIndex,
93+
numEmployees, request.CancellationToken);
94+
95+
return new ItemsProviderResult<Employee>(employees, totalEmployees);
96+
}
97+
```
98+
99+
## Placeholder
100+
101+
Because requesting items from a remote data source might take some time, you have the option to render a placeholder (`<Placeholder>...</Placeholder>`) until the item data is available:
102+
103+
```razor
104+
<table>
105+
<Virtualize Context="employee" ItemsProvider="@LoadEmployees">
106+
<ItemContent>
107+
<tr>
108+
<td>@employee.FirstName</td>
109+
<td>@employee.LastName</td>
110+
<td>@employee.JobTitle</td>
111+
</tr>
112+
</ItemContent>
113+
<Placeholder>
114+
<tr>
115+
<td>Loading...</td>
116+
</tr>
117+
</Placeholder>
118+
</Virtualize>
119+
</table>
120+
```
121+
122+
## Item size
123+
124+
The size of each item in pixels can be set with `ItemSize` (default: 50px):
125+
126+
```razor
127+
<table>
128+
<Virtualize Context="employee" Items="@employees" ItemSize="25">
129+
...
130+
</Virtualize>
131+
</table>
132+
```
133+
134+
## Overscan count
135+
136+
`OverscanCount` determines how many additional items are rendered before and after the visible region. This setting helps to reduce the frequency of rendering during scrolling. However, higher values result in more elements rendered in the page (default: 3):
137+
138+
```razor
139+
<table>
140+
<Virtualize Context="employee" Items="@employees" OverscanCount="4">
141+
...
142+
</Virtualize>
143+
</table>
144+
```
145+
146+
::: moniker-end
147+
148+
::: moniker range="< aspnetcore-5.0"
149+
150+
For example, a grid or list that renders hundreds of rows containing components is processor intensive to render. Consider virtualizing a grid or list layout so that only a subset of the components is rendered at any given time. For an example of component subset rendering, see the following components in the [`Virtualization` sample app (aspnet/samples GitHub repository)](https://github.com/aspnet/samples/tree/master/samples/aspnetcore/blazor/Virtualization):
151+
152+
* `Virtualize` component ([`Shared/Virtualize.razor`](https://github.com/aspnet/samples/blob/master/samples/aspnetcore/blazor/Virtualization/Shared/Virtualize.cs)): A component written in C# that implements <xref:Microsoft.AspNetCore.Components.ComponentBase> to render a set of weather data rows based on user scrolling.
153+
* `FetchData` component ([`Pages/FetchData.razor`](https://github.com/aspnet/samples/blob/master/samples/aspnetcore/blazor/Virtualization/Pages/FetchData.razor)): Uses the `Virtualize` component to display 25 rows of weather data at a time.
154+
155+
::: moniker-end

aspnetcore/blazor/webassembly-performance-best-practices.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,7 @@ For more information, see <xref:blazor/components/lifecycle#after-component-rend
6565

6666
Components offer a convenient approach to produce re-usable fragments of code and markup. In general, we recommend authoring individual components that best align with the app's requirements. One caveat is that each additional child component contributes to the total time it takes to render a parent component. For most apps, the additional overhead is negligible. Apps that produce a large number of components should consider using strategies to reduce processing overhead, such as limiting the number of rendered components.
6767

68-
For example, a grid or list that renders hundreds of rows containing components is processor intensive to render. Consider virtualizing a grid or list layout so that only a subset of the components is rendered at any given time. For an example of component subset rendering, see the following components in the [`Virtualization` sample app (aspnet/samples GitHub repository)](https://github.com/aspnet/samples/tree/master/samples/aspnetcore/blazor/Virtualization):
69-
70-
* `Virtualize` component ([`Shared/Virtualize.razor`](https://github.com/aspnet/samples/blob/master/samples/aspnetcore/blazor/Virtualization/Shared/Virtualize.cs)): A component written in C# that implements <xref:Microsoft.AspNetCore.Components.ComponentBase> to render a set of weather data rows based on user scrolling.
71-
* `FetchData` component ([`Pages/FetchData.razor`](https://github.com/aspnet/samples/blob/master/samples/aspnetcore/blazor/Virtualization/Pages/FetchData.razor)): Uses the `Virtualize` component to display 25 rows of weather data at a time.
68+
For more information, see <xref:blazor/components/virtualization>.
7269

7370
## Avoid JavaScript interop to marshal data
7471

aspnetcore/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@
371371
uid: blazor/components/event-handling
372372
- name: Lifecycle
373373
uid: blazor/components/lifecycle
374+
- name: Component virtualization
375+
uid: blazor/components/virtualization
374376
- name: Templated components
375377
uid: blazor/components/templated-components
376378
- name: Integrate components

0 commit comments

Comments
 (0)