wirtualizacja składników ASP.NET Core Razor
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję artykułu .NET 9.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zobaczyć bieżącą wersję, zobacz wersję tego artykułu platformy .NET 9.
W tym artykule wyjaśniono, jak używać wirtualizacji składników w aplikacjach ASP.NET Core Blazor .
Wirtualizacja
Zwiększ postrzeganą wydajność renderowania składników, korzystając z wbudowanej w Blazor obsługi wirtualizacji platformy oraz składnika Virtualize<TItem>. Wirtualizacja to technika ograniczania renderowania interfejsu użytkownika tylko do części, które są obecnie widoczne. Na przykład wirtualizacja jest przydatna, gdy aplikacja musi renderować długą listę elementów, a tylko podzbiór elementów musi być widoczny w danym momencie.
Użyj składnika Virtualize<TItem> gdy:
- Renderowanie zestawu elementów danych w pętli.
- Większość elementów nie jest widoczna z powodu przewijania.
- Renderowane elementy mają ten sam rozmiar.
Gdy użytkownik przewija dowolny punkt na Virtualize<TItem> liście elementów składnika, składnik oblicza widoczne elementy do pokazania. Niewidoczne elementy nie są renderowane.
Bez wirtualizacji typowa lista może używać pętli języka C# foreach
do renderowania każdego elementu na liście. W poniższym przykładzie:
-
allFlights
jest zbiorem lotów samolotowych. - Składnik
FlightSummary
wyświetla szczegółowe informacje o każdym locie. - Atrybut
@key
dyrektywy zachowuje relację każdegoFlightSummary
składnika z jego renderowanym lotem według parametrów lotuFlightId
.
<div style="height:500px;overflow-y:scroll">
@foreach (var flight in allFlights)
{
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
}
</div>
Jeśli kolekcja zawiera tysiące lotów, renderowanie lotów trwa długo, a użytkownicy doświadczają zauważalnego opóźnienia interfejsu użytkownika. Większość lotów znajduje się poza wysokością elementu <div>
, dlatego większość z nich nie jest widoczna.
Zamiast renderowania całej listy lotów jednocześnie, zastąp pętlę foreach
w poprzednim przykładzie składnikiem Virtualize<TItem> :
Określ
allFlights
jako źródło elementu stałego dla Virtualize<TItem>.Items. Tylko aktualnie widoczne loty są renderowane przez składnik Virtualize<TItem>.Jeśli kolekcja niegeneryczna dostarcza elementy, na przykład kolekcja DataRow, postępuj zgodnie ze wskazówkami zawartymi w sekcji delegata dostarczającego elementy, aby zapewnić elementy.
Określ kontekst dla każdego lotu za pomocą parametru
Context
. W poniższym przykładzieflight
jest używany jako kontekst, który zapewnia dostęp do członków każdego lotu.
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights" Context="flight">
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
</Virtualize>
</div>
Jeśli kontekst nie jest określony za pomocą parametru Context
, użyj wartości context
w szablonie zawartości elementu, aby uzyskać dostęp do elementów członkowskich każdego lotu:
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights">
<FlightSummary @key="context.FlightId" Details="@context.Summary" />
</Virtualize>
</div>
Składnik Virtualize<TItem> :
- Oblicza liczbę elementów renderowanych na podstawie wysokości kontenera i rozmiaru renderowanych elementów.
- Ponownie oblicza i przetwarza elementy, gdy użytkownik przewija.
- Pobiera tylko fragment rekordów z zewnętrznego interfejsu API, który odpowiada obecnie widocznemu regionie, w tym przeskanowania, gdy
ItemsProvider
jest używany zamiastItems
(zobacz sekcję Delegat dostawcy elementów).
Zawartość elementu dla Virtualize<TItem> składnika może obejmować:
- Czysty HTML i Razor kod, jak pokazano w poprzednim przykładzie.
- Jeden lub więcej Razor składników.
- Mieszanka kodu HTML/Razor i Razor składników.
Delegat dostawcy elementów
Jeśli nie chcesz załadować wszystkich elementów do pamięci lub kolekcja nie jest ogólnym ICollection<T>, możesz określić metodę delegata dostawcy elementów jako parametr składnika Virtualize<TItem>.ItemsProvider, który asynchronicznie pobiera żądane elementy na żądanie. W poniższym przykładzie LoadEmployees
metoda udostępnia elementy składnikowi Virtualize<TItem> :
<Virtualize Context="employee" ItemsProvider="LoadEmployees">
<p>
@employee.FirstName @employee.LastName has the
job title of @employee.JobTitle.
</p>
</Virtualize>
Dostawca elementów otrzymuje ItemsProviderRequest, który określa wymaganą liczbę elementów, zaczynając od określonego indeksu początkowego. Dostawca elementów pobiera następnie żądane elementy z bazy danych lub innej usługi i zwraca je jako wartość ItemsProviderResult<TItem> wraz z liczbą wszystkich elementów. Dostawca elementów może pobrać elementy z każdym żądaniem lub zapisać je w pamięci podręcznej, aby były łatwo dostępne.
Virtualize<TItem> Składnik może akceptować tylko jedno źródło z jego parametrów, więc nie próbuj jednocześnie używać dostawcy elementów i przypisywać kolekcji do Items
. Jeśli oba są przypisane, zgłaszany jest InvalidOperationException, gdy parametry składnika są ustawiane w czasie wykonywania.
Poniższy przykład ładuje pracowników z elementu EmployeeService
(nie pokazano). Pole totalEmployees
zazwyczaj jest przypisywane przez wywołanie metody w tej samej usłudze, na przykład EmployeesService.GetEmployeesCountAsync
, w innym miejscu, na przykład podczas inicjowania składnika.
private async ValueTask<ItemsProviderResult<Employee>> LoadEmployees(
ItemsProviderRequest request)
{
var numEmployees = Math.Min(request.Count, totalEmployees - request.StartIndex);
var employees = await EmployeesService.GetEmployeesAsync(request.StartIndex,
numEmployees, request.CancellationToken);
return new ItemsProviderResult<Employee>(employees, totalEmployees);
}
W poniższym przykładzie kolekcja DataRow jest kolekcją niegeneryjną, więc delegat dostawcy elementów jest używany do wirtualizacji:
<Virtualize Context="row" ItemsProvider="GetRows">
...
</Virtualize>
@code{
...
private ValueTask<ItemsProviderResult<DataRow>> GetRows(ItemsProviderRequest request) =>
new(new ItemsProviderResult<DataRow>(
dataTable.Rows.OfType<DataRow>().Skip(request.StartIndex).Take(request.Count),
dataTable.Rows.Count));
}
Virtualize<TItem>.RefreshDataAsync instruuje składnik do ponownego pobierania danych z ItemsProvider. Jest to przydatne, gdy dane zewnętrzne zmieniają się. Zwykle nie ma potrzeby wywoływania RefreshDataAsync w przypadku korzystania z Items.
RefreshDataAsync
Virtualize<TItem> aktualizuje dane składnika bez powodowania jego ponownego renderowania. Jeśli RefreshDataAsync jest wywoływana z Blazor procedury obsługi zdarzeń lub metody cyklu życia składnika, wyzwalanie renderowania nie jest wymagane, ponieważ renderowanie jest automatycznie wyzwalane na końcu procedury obsługi zdarzeń lub metody cyklu życia. Jeśli RefreshDataAsync jest uruchamiane oddzielnie od zadania lub zdarzenia w tle, na przykład jak w następującym delegacie ForecastUpdated
, wywołaj metodę StateHasChanged na końcu tego zadania lub zdarzenia w tle, aby zaktualizować interfejs użytkownika.
<Virtualize ... @ref="virtualizeComponent">
...
</Virtualize>
...
private Virtualize<FetchData>? virtualizeComponent;
protected override void OnInitialized()
{
WeatherForecastSource.ForecastUpdated += async () =>
{
await InvokeAsync(async () =>
{
await virtualizeComponent?.RefreshDataAsync();
StateHasChanged();
});
});
}
W powyższym przykładzie:
- RefreshDataAsync element jest wywoływany jako pierwszy w celu uzyskania nowych danych dla Virtualize<TItem> składnika.
-
StateHasChanged
jest wywoływany w celu ponownego renderowania składnika.
Symbol zastępczy
Żądanie elementów ze zdalnego źródła danych może zająć trochę czasu, dlatego istnieje możliwość renderowania symbolu zastępczego z zawartością elementu:
- Użyj znaku Placeholder (
<Placeholder>...</Placeholder>
), aby wyświetlić zawartość do momentu udostępnienia danych elementu. - Użyj Virtualize<TItem>.ItemContent polecenia , aby ustawić szablon elementu dla listy.
<Virtualize Context="employee" ItemsProvider="LoadEmployees">
<ItemContent>
<p>
@employee.FirstName @employee.LastName has the
job title of @employee.JobTitle.
</p>
</ItemContent>
<Placeholder>
<p>
Loading…
</p>
</Placeholder>
</Virtualize>
Pusta zawartość
Użyj parametru , EmptyContent aby podać zawartość, gdy składnik został załadowany i Items jest pusty lub ItemsProviderResult<TItem>.TotalItemCount ma wartość zero.
EmptyContent.razor
:
@page "/empty-content"
<PageTitle>Empty Content</PageTitle>
<h1>Empty Content Example</h1>
<Virtualize Items="stringList">
<ItemContent>
<p>
@context
</p>
</ItemContent>
<EmptyContent>
<p>
There are no strings to display.
</p>
</EmptyContent>
</Virtualize>
@code {
private List<string>? stringList;
protected override void OnInitialized() => stringList ??= [];
}
@page "/empty-content"
<PageTitle>Empty Content</PageTitle>
<h1>Empty Content Example</h1>
<Virtualize Items="stringList">
<ItemContent>
<p>
@context
</p>
</ItemContent>
<EmptyContent>
<p>
There are no strings to display.
</p>
</EmptyContent>
</Virtualize>
@code {
private List<string>? stringList;
protected override void OnInitialized() => stringList ??= [];
}
Zmień metodę lambda OnInitialized
, aby zobaczyć, jak komponent wyświetla teksty.
protected override void OnInitialized() =>
stringList ??= [ "Here's a string!", "Here's another string!" ];
Rozmiar elementu
Wysokość każdego elementu w pikselach można ustawić za pomocą Virtualize<TItem>.ItemSize (domyślnie: 50). Poniższy przykład zmienia wysokość każdego elementu z wartości domyślnej 50 pikseli na 25 pikseli:
<Virtualize Context="employee" Items="employees" ItemSize="25">
...
</Virtualize>
Składnik Virtualize<TItem> mierzy rozmiar renderowania (wysokość) poszczególnych elementów po początkowym renderowaniu. Użyj ItemSize do podania z góry dokładnego rozmiaru elementu, aby ułatwić dokładne początkowe renderowanie i zapewnić poprawną pozycję przewijania podczas ponownego ładowania strony. Jeśli ustawienie domyślne ItemSize powoduje, że niektóre elementy będą renderowane poza obecnie widocznym widokiem, zostanie wyzwolony drugi rerender. Aby poprawnie zachować położenie przewijania przeglądarki na zwirtualizowanej liście, początkowy render musi być prawidłowy. Jeśli nie, użytkownicy mogą wyświetlać nieprawidłowe elementy.
Liczba przeskanów
Virtualize<TItem>.OverscanCount określa liczbę dodatkowych elementów renderowanych przed i po widocznym regionie. To ustawienie pomaga zmniejszyć częstotliwość renderowania podczas przewijania. Jednak wyższe wartości powodują więcej elementów renderowanych na stronie (wartość domyślna: 3). Poniższy przykład zmienia liczbę przeskanów z wartości domyślnej trzech elementów na cztery elementy:
<Virtualize Context="employee" Items="employees" OverscanCount="4">
...
</Virtualize>
Zmiany stanu
Podczas wprowadzania zmian w elementach renderowanych przez składnik Virtualize<TItem>, należy wywołać StateHasChanged, aby dodać komponent do kolejki w celu ponownej oceny i ponownego renderowania. Aby uzyskać więcej informacji, zobacz Renderowanie składników platformy ASP.NET Core Razor.
Obsługa przewijania klawiatury
Aby umożliwić użytkownikom przewijanie zwirtualizowanej zawartości przy użyciu klawiatury, upewnij się, że zwirtualizowane elementy lub sam kontener przewijania można skupić. Jeśli nie zrobisz tego kroku, przewijanie klawiatury nie działa w przeglądarkach opartych na chromium.
Można na przykład użyć atrybutu tabindex
do kontenera przewijania.
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights">
<div class="flight-info">...</div>
</Virtualize>
</div>
Aby dowiedzieć się więcej o znaczeniu wartości tabindex
, wartości -1
, wartości 0
lub innych wartości, zobacz tabindex
(dokumentacja MDN).
Zaawansowane style i detekcja przewijania
Składnik Virtualize<TItem> jest przeznaczony tylko do obsługi określonych mechanizmów układu elementów. Aby zrozumieć, które układy elementów działają poprawnie, w poniższym artykule wyjaśniono, jak Virtualize
wykrywać, które elementy powinny być widoczne dla wyświetlania we właściwym miejscu.
Jeśli kod źródłowy wygląda następująco:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights" ItemSize="100">
<div class="flight-info">Flight @context.Id</div>
</Virtualize>
</div>
W czasie wykonywania Virtualize<TItem> składnik renderuje strukturę DOM podobną do następującej:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<div style="height:1100px"></div>
<div class="flight-info">Flight 12</div>
<div class="flight-info">Flight 13</div>
<div class="flight-info">Flight 14</div>
<div class="flight-info">Flight 15</div>
<div class="flight-info">Flight 16</div>
<div style="height:3400px"></div>
</div>
Rzeczywista liczba renderowanych wierszy i rozmiar odstępów różnią się w zależności od stylu i rozmiaru kolekcji Items
. Należy jednak zauważyć, że istnieją elementy odstępu div
wprowadzone przed i po zawartości. Służą one dwóm celom:
- Aby zapewnić przesunięcie przed zawartością i po niej, co spowoduje, że bieżące widoczne elementy będą wyświetlane w odpowiedniej lokalizacji w zakresie przewijania, a zakres przewijania będzie reprezentować całkowity rozmiar całej zawartości.
- Aby wykryć, kiedy użytkownik przewija się poza bieżący widoczny zakres, co oznacza, że inna zawartość musi być renderowana.
Uwaga
Aby dowiedzieć się, jak kontrolować tag elementu spacer HTML, zobacz sekcję Kontrolowanie nazwy tagu elementu spacer w dalszej części tego artykułu.
Elementy odstępu wewnętrznie używają obserwatora skrzyżowania do odbierania powiadomień, gdy staną się widoczne.
Virtualize
zależy od odbierania tych zdarzeń.
Virtualize
działa w następujących warunkach:
Wszystkie renderowane elementy treści, w tym treść zastępcza, mają identyczną wysokość. Dzięki temu można obliczyć, która zawartość odpowiada danej pozycji przewijania bez uprzedniego pobierania każdego elementu danych i renderowania danych w elemencie DOM.
Zarówno odstępy, jak i wiersze zawartości są renderowane w jednym pionowym stosie z każdym elementem wypełniającym całą szerokość poziomą. W typowych przypadkach
Virtualize
działa z elementamidiv
. Jeśli używasz arkuszy CSS do utworzenia bardziej zaawansowanego układu, pamiętaj o następujących wymaganiach:- Styl przewijania kontenera wymaga elementu
display
z dowolną z następujących wartości:-
block
(wartość domyślna dla elementudiv
). -
table-row-group
(wartość domyślna dla elementutbody
). -
flex
z ustawioną wartościąflex-direction
column
. Upewnij się, że bezpośrednie elementy podrzędne Virtualize<TItem> składnika nie kurczą się zgodnie z zasadami flex. Na przykład dodaj nazwę.mycontainer > div { flex-shrink: 0 }
.
-
- Styl wiersza zawartości wymaga elementu
display
z jedną z następujących wartości:-
block
(wartość domyślna dla elementudiv
). -
table-row
(wartość domyślna dla elementutr
).
-
- Nie używaj CSS do zakłócania układu elementów odstępowych. Elementy odstępu mają
display
wartośćblock
, z wyjątkiem sytuacji, gdy element nadrzędny jest grupą wierszy tabeli, przyjmuje wówczas domyślnie wartośćtable-row
. Nie próbuj wpływać na szerokość lub wysokość elementu odstępu, w tym przez dodanie im obramowania lubcontent
pseudoelementów.
- Styl przewijania kontenera wymaga elementu
Każde podejście, które uniemożliwia renderowanie elementów odstępu i zawartości jako pojedynczego pionowego stosu lub powoduje, że elementy zawartości różnią się wysokością, uniemożliwia prawidłowe działanie składnika Virtualize<TItem>.
Wirtualizacja na poziomie głównym
Składnik Virtualize<TItem> obsługuje używanie samego dokumentu jako głównego kontenera przewijania, jako alternatywę dla posiadania innego elementu z overflow-y: scroll
. W poniższym przykładzie elementy <html>
lub <body>
są stylizowane w składniku z użyciem overflow-y: scroll
.
<HeadContent>
<style>
html, body { overflow-y: scroll }
</style>
</HeadContent>
Składnik Virtualize<TItem> obsługuje używanie samego dokumentu jako korzenia przewijania, jako alternatywę dla posiadania innego elementu z overflow-y: scroll
. W przypadku używania dokumentu jako głównego korzenia przewijania należy unikać stylizowania elementów <html>
lub <body>
z overflow-y: scroll
, ponieważ powoduje to, że obserwator przecięcia traktuje pełną wysokość strony jako widoczny obszar, zamiast tylko okna widoku.
Ten problem można odtworzyć, tworząc dużą listę zwirtualizowaną (na przykład 100 000 elementów) i próbując użyć dokumentu jako korzenia przewijania z html { overflow-y: scroll }
w stylach CSS strony. Chociaż czasami może działać poprawnie, przeglądarka próbuje renderować wszystkie 100 000 elementów co najmniej raz na początku renderowania, co może spowodować zablokowanie karty przeglądarki.
Aby obejść ten problem przed wydaniem programu .NET 7, należy unikać tworzenia stylów <html>
/<body>
elementów za pomocą overflow-y: scroll
lub przyjąć alternatywne podejście. W poniższym przykładzie wysokość <html>
elementu jest ustawiona na nieco ponad 100% wysokości widoku:
<HeadContent>
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
</HeadContent>
Składnik Virtualize<TItem> obsługuje używanie samego dokumentu jako głównego elementu przewijania, jako alternatywę dla innego elementu z overflow-y: scroll
. W przypadku użycia dokumentu jako głównego elementu przewijania należy unikać stylizowania elementów <html>
lub <body>
za pomocą overflow-y: scroll
, ponieważ powoduje to, że cała przewijalna wysokość strony jest traktowana jako widoczny region, zamiast jedynie okna widoku.
Ten problem można odtworzyć, tworząc zwirtualizowaną listę (na przykład 100 000 elementów) i próbując użyć dokumentu jako korzenia przewijania z html { overflow-y: scroll }
w stylach CSS strony. Chociaż czasami może działać poprawnie, przeglądarka próbuje renderować wszystkie 100 000 elementów co najmniej raz na początku renderowania, co może spowodować zablokowanie karty przeglądarki.
Aby obejść ten problem przed wydaniem programu .NET 7, należy unikać tworzenia stylów <html>
/<body>
elementów za pomocą overflow-y: scroll
lub przyjąć alternatywne podejście. W poniższym przykładzie wysokość <html>
elementu jest ustawiona na nieco ponad 100% wysokości widoku:
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
Kontrolowanie nazwy tagu elementu spacer
Jeśli składnik Virtualize<TItem> zostanie umieszczony wewnątrz elementu, który wymaga określonej nazwy tagu podrzędnego, SpacerElement pozwala uzyskać lub ustawić nazwę tagu odstępnika wirtualizacji. Domyślna wartość to div
. W poniższym przykładzie komponent Virtualize<TItem> jest renderowany wewnątrz elementu treści tabeli (tbody
), więc odpowiedni element potomny dla wiersza tabeli (tr
) jest ustawiony jako odstęp.
VirtualizedTable.razor
:
@page "/virtualized-table"
<PageTitle>Virtualized Table</PageTitle>
<HeadContent>
<style>
html, body {
overflow-y: scroll
}
</style>
</HeadContent>
<h1>Virtualized Table Example</h1>
<table id="virtualized-table">
<thead style="position: sticky; top: 0; background-color: silver">
<tr>
<th>Item</th>
<th>Another column</th>
</tr>
</thead>
<tbody>
<Virtualize Items="fixedItems" ItemSize="30" SpacerElement="tr">
<tr @key="context" style="height: 30px;" id="row-@context">
<td>Item @context</td>
<td>Another value</td>
</tr>
</Virtualize>
</tbody>
</table>
@code {
private List<int> fixedItems = Enumerable.Range(0, 1000).ToList();
}
W poprzednim przykładzie korzeń dokumentu jest używany jako kontener przewijania, więc elementy html
i body
są stylizowane za pomocą overflow-y: scroll
. Aby uzyskać więcej informacji, zobacz następujące zasoby: