wirtualizacja składników ASP.NET Core Razor

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

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 zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

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 przy użyciu Blazor wbudowanej obsługi wirtualizacji platformy z składnikiem 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.

Virtualize<TItem> Użyj składnika, 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. Niezaznaczone 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 kolekcją lotów samolotowych.
  • Składnik FlightSummary wyświetla szczegółowe informacje o każdym locie.
  • Atrybut @key dyrektywy zachowuje relację każdego FlightSummary składnika do jego renderowanego lotu przez lot FlightId.
<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 nie jest widoczna, ponieważ wypadają poza wysokość <div> elementu.

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 na Virtualize<TItem>.Items. Składnik renderuje Virtualize<TItem> tylko aktualnie widoczne loty.

    Jeśli kolekcja niegeneryczna dostarcza elementy, na przykład kolekcję DataRow, postępuj zgodnie ze wskazówkami w sekcji delegata dostawcy elementów, aby podać elementy.

  • Określ kontekst dla każdego lotu za pomocą parametru Context . W poniższym przykładzie flight 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 pakietu:

<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 rerenders elementy podczas przewijania użytkownika.
  • Pobiera tylko fragment rekordów z zewnętrznego interfejsu API, który odpowiada obecnie widocznemu regionie, w tym przeskanowania, gdy ItemsProvider jest używany zamiast Items (zobacz sekcję Delegat dostawcy elementów).

Zawartość elementu dla Virtualize<TItem> składnika może obejmować:

  • Zwykły kod HTML i Razor kod, jak pokazano w poprzednim przykładzie.
  • Razor Co najmniej jeden składnik.
  • 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ólną ICollection<T>metodą , możesz określić metodę delegata dostawcy elementów do parametru 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 element , który określa wymaganą ItemsProviderRequestliczbę elementów rozpoczynających się 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 elementu z jego parametrów, więc nie próbuj jednocześnie używać dostawcy elementów i przypisywać kolekcję do Itemselementu . Jeśli oba są przypisane, parametr jest zgłaszany, InvalidOperationException gdy parametry składnika są ustawiane w czasie wykonywania.

Poniższy przykład ładuje pracowników z elementu EmployeeService (nie pokazano):

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)
    {
        return 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 jego ItemsProviderelementu . Jest to przydatne, gdy dane zewnętrzne zmieniają się. Zwykle nie ma potrzeby wywoływania RefreshDataAsync w przypadku korzystania z polecenia Items.

RefreshDataAsyncVirtualize<TItem> aktualizuje dane składnika bez powodowania rerender. 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 wyzwalane jest oddzielnie od zadania lub zdarzenia w tle, takiego jak w następującym ForecastUpdated delegatu, wywołaj metodę StateHasChanged , aby zaktualizować interfejs użytkownika na końcu zadania lub zdarzenia w tle:

<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 element jest wywoływany w celu rerender 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&hellip;
        </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 ??= new();
}

Zmień metodę lambda, OnInitialized aby wyświetlić ciągi wyświetlania składnika:

protected override void OnInitialized() =>
    stringList ??= new() { "Here's a string!", "Here's another string!" };

Rozmiar elementu

Wysokość każdego elementu w pikselach można ustawić za pomocą Virtualize<TItem>.ItemSize wartości (wartość domyślna: 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>

Domyślnie Virtualize<TItem> składnik mierzy rozmiar renderowania (wysokość) poszczególnych elementów po początkowym renderowaniu. Użyj ItemSize polecenia , aby zapewnić dokładny rozmiar elementu z wyprzedzeniem, aby ułatwić dokładną początkową wydajność renderowania i zapewnić poprawną pozycję przewijania strony do ponownego ładowania strony. Jeśli ustawienie domyślne ItemSize powoduje renderowanie niektórych elementów poza obecnie widocznym widokiem, zostanie wyzwolony drugi ponowny renderowanie. Aby poprawnie zachować położenie przewijania przeglądarki na zwirtualizowanej liście, początkowy renderowanie musi być poprawne. 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 Virtualize<TItem> składnik wywołaj metodę StateHasChanged wymuszenia ponownej oceny i rerendering składnika. 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ć tabindex atrybutu w kontenerze 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 , lub innych wartości, zobacz tabindex (dokumentacja MDN).. 0-1tabindex

Zaawansowane style i wykrywanie 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 spacerów różnią się w zależności od stylu i Items rozmiaru kolekcji. Należy jednak zauważyć, że istnieją elementy odstępu div wprowadzone przed i po zawartości. Służą one dwóm celom:

  • Aby podać przesunięcie przed zawartością i po niej, powodując, że aktualnie widoczne elementy będą wyświetlane w prawidłowej lokalizacji w zakresie przewijania i sam zakres przewijania reprezentujący 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 zawartości, w tym zawartość 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ą. Jest to zazwyczaj wartość domyślna. W typowych przypadkach z elementami divVirtualize działa domyślnie. 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 elementu div).
      • table-row-group (wartość domyślna dla elementu tbody).
      • flex z ustawioną wartością flex-directioncolumn. 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 elementu div).
      • table-row (wartość domyślna dla elementu tr).
    • Nie należy używać arkuszy CSS do zakłócania układu elementów odstępu. Domyślnie elementy odstępu mają wartość block, z wyjątkiem sytuacji, gdy element nadrzędny jest grupą display wierszy tabeli, w tym przypadku domyślnie mają table-rowwartość . Nie próbuj wpływać na szerokość lub wysokość elementu odstępu, w tym przez spowodowanie, że mają obramowanie lub content pseudoelementy.

Każde podejście, które uniemożliwia renderowanie elementów odstępów i zawartości jako pojedynczego stosu pionowego lub powoduje, że elementy zawartości różnią się w wysokości, uniemożliwiają prawidłowe działanie Virtualize<TItem> składnika.

Wirtualizacja na poziomie głównym

Składnik Virtualize<TItem> obsługuje używanie samego dokumentu jako katalogu głównego przewijania, jako alternatywę dla innego elementu z elementem overflow-y: scroll. W poniższym przykładzie <html> elementy lub <body> są stylowane w składniku z elementem overflow-y: scroll:

<HeadContent>
    <style>
        html, body { overflow-y: scroll }
    </style>
</HeadContent>

Składnik Virtualize<TItem> obsługuje używanie samego dokumentu jako katalogu głównego przewijania, jako alternatywę dla innego elementu z elementem overflow-y: scroll. W przypadku używania dokumentu jako katalogu głównego przewijania należy unikać stylów <html> lub elementów, overflow-y: scroll ponieważ powoduje, że obserwator skrzyżowania traktuje pełną wysokość strony jako widoczny region, a nie tylko <body> okno viewport.

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 katalogu głównego html { overflow-y: scroll } przewijania 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 katalogu głównego przewijania, jako alternatywę dla innego elementu z elementem overflow-y: scroll. W przypadku używania dokumentu jako katalogu głównego przewijania należy unikać stylów <html> lub <body> elementów, overflow-y: scroll ponieważ powoduje, że pełna wysokość strony może być traktowana jako widoczny region, a nie tylko w oknie 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 katalogu głównego html { overflow-y: scroll } przewijania 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

Virtualize<TItem> Jeśli składnik zostanie umieszczony wewnątrz elementu, który wymaga określonej nazwy tagu podrzędnego, SpacerElement umożliwia uzyskanie lub ustawienie nazwy tagu spacer wirtualizacji. Domyślna wartość to div. W poniższym przykładzie Virtualize<TItem> składnik renderuje się wewnątrz elementu treści tabeli (tbody), więc odpowiedni element podrzędny 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 główny dokument jest używany jako kontener przewijania, więc html elementy i body są stylizowany na overflow-y: scroll. Aby uzyskać więcej informacji, zobacz następujące zasoby: