wirtualizacja składników ASP.NET Core Razor

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby uzyskać bieżącą wersję, zobacz wersję .NET 10 tego artykułu.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy. Aby uzyskać bieżącą wersję, zobacz wersję .NET 10 tego artykułu.

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.

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żdego FlightSummary składnika z jego renderowanym lotem według parametrów lotu 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 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ł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 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 regionowi, w tym obszar nadskanowania, kiedy stosuje się ItemsProvider zamiast Items (zobacz Sekcja 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ą niegeneryczną, dlatego dla wirtualizacji używany jest delegat dostawcy elementów.

<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&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 ??= [];
}
@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 początkowo ustawić za pomocą Virtualize<TItem>.ItemSize (domyślnie: 50). W poniższym przykładzie ustawia początkową wysokość każdego elementu z 50 pikseli na 25 pikseli:

<Virtualize Context="employee" Items="employees" ItemSize="25">
    ...
</Virtualize>

Składnik Virtualize<TItem> mierzy rzeczywiste wysokości elementów, gdy wchodzą do widoku, i cały czas aktualizuje średnią z mierzonymi wysokościami. Wszystkie elementy używają tej średniej bieżącej do pozycjonowania (lub parametru domyślnego ItemSize przed istniejącymi pomiarami).

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 nadmiarowych skanó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: 15). Poniższy przykład zmienia liczbę przeskanów z wartości domyślnej 15 elementów na 17 elementów:

<Virtualize Context="employee" Items="employees" OverscanCount="17">
    ...
</Virtualize>

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>

Przewiń do określonego elementu

Składnik Virtualize<TItem> udostępnia dwa sposoby kontrolowania pozycji przewijania: InitialItemIndex dla pierwszego renderowania oraz ScrollToItemAsync do programowego przewijania po wyrenderowaniu składnika.

InitialItemIndex Parametr

Ustaw InitialItemIndex, aby otworzyć listę na elemencie o określonym indeksie podczas pierwszego interaktywnego renderowania. Jest to parametr jednorazowy — zmiany po pierwszym renderowaniu są ignorowane. Wartości poza zakresem są zaciśnięte.

<Virtualize Items="allFlights" Context="flight" InitialItemIndex="500">
    <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
</Virtualize>

Metoda ScrollToItemAsync

Wywołaj metodę ScrollToItemAsync , aby programowo przewinąć element po pierwszym renderowaniu. Przewijanie jest natychmiastowe (bez animacji). Metoda zwraca obiekt Task, który kończy działanie, gdy element docelowy zostanie wyrównany do górnej krawędzi obszaru widoku. Anulowanie jest obsługiwane za pośrednictwem elementu CancellationToken.

Jeśli wystąpi wiele wywołań, decydujące jest ostatnie wywołanie — wcześniejsze wywołania kończą się normalnie, ale uwzględniany jest tylko ostatni cel. Jeśli użytkownik przewija się podczas przewijania programowego, przewijanie użytkownika ma pierwszeństwo. Wywołanie przed pierwszym interakcyjnym renderowaniem zgłasza błąd InvalidOperationException.

<Virtualize Items="allFlights" Context="flight" @ref="virtualizeComponent">
    <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
</Virtualize>

<button @onclick="ScrollToFlight">Go to flight 200</button>

@code {
    private Virtualize<Flight>? virtualizeComponent;

    private async Task ScrollToFlight()
    {
        if (virtualizeComponent is not null)
        {
            await virtualizeComponent.ScrollToItemAsync(200);
        }
    }
}

Sterowanie zachowaniem pozycji przewijania w obszarze widoku, gdy elementy są dynamicznie dodawane

VirtualizeAnchorMode Przypisz wartość do parametru, AnchorMode aby kontrolować sposób działania widoku na krawędziach listy, gdy elementy są dynamicznie dodawane:

  • None: brak przypinania krawędzi. Obszar widoku pozostaje na bieżącej pozycji przewijania niezależnie od zmian elementów.
  • Start: Przypina obszar widoku do początku listy. Gdy użytkownik znajduje się w pozycji przewijania w górnej części listy i pojawiają się nowe elementy na początku, okienko widoku pozostaje w górnej części pokazujące najnowsze elementy. Na przykład to działanie przypinania jest przydatne w interfejsie kanału aktualności.
  • End: Przypina obszar widoku na końcu listy. Gdy użytkownik znajduje się na pozycji przewinięcia blisko końca listy, a na jej końcu pojawiają się nowe elementy, widok automatycznie przewija się, aby je wyświetlić. Jeśli użytkownik przewinie widok z dala od dołu, automatyczne przewijanie wyłącza się, dopóki nie wróci on na dół. Na przykład taki sposób przypinania jest przydatny w interfejsie czatu lub dziennika zdarzeń.

Poniższy przykład przypina obszar widoku do początku zwirtualizowanej listy lotów:

<div style="height:500px;overflow-y:scroll">
    <Virtualize Items="allFlights" Context="flight" AnchorMode="Start">
        <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
    </Virtualize>
</div>

Tryby można łączyć. Na przykład przypisanie Start | End przypina obie krawędzie. None Łączenie z innymi trybami jest obsługiwane, ale nie zmienia połączonej wartości.

Virtualize.ItemComparer pobiera lub ustawia obiekt porównujący używany do wykrywania, czy elementy zostały dodane na początku czy na końcu podczas używania elementów typu klasowego z elementem ItemsProvider (aby uzyskać więcej informacji, zobacz sekcję Delegat dostawcy elementów).

Komparator określa, czy pierwszy załadowany element zmienił się między kolejnymi wywołaniami dostawcy, co wskazuje, że elementy zostały wstawione na początku. W przypadku rekordów zachowanie domyślnego komparatora polegające na porównywaniu wartości (EqualityComparer<T>.Default) działa automatycznie. W przypadku przypisania Items w pamięci moduł porównywania ItemComparer nie jest wymagany, ponieważ komponent może automatycznie wykrywać elementy dodane na początku. W przypadkach, gdy nieprymitywne obiekty są wirtualizowane, a framework nie może wykryć, czy element został dodany na początku czy na końcu, przypisz element IEqualityComparer<T> do komponentu Virtualize:

<Virtualize ItemsProvider="LoadFlights" AnchorMode="Start" 
    ItemComparer="itemComparer">
    ...
</Virtualize>

@code {
    private static readonly IEqualityComparer<Flight> itemComparer =
        EqualityComparer<Flight>.Create((a, b) => 
            a.Index == b.Index, item => item.Index);

    private async ValueTask<ItemsProviderResult<Flight>> LoadFlights(
        ItemsProviderRequest request)
    {
        ...
    }
}

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 ASP.NET Core Razor component rendering.

Obsługa przewijania klawiatury

Aby umożliwić użytkownikom przewijanie zwirtualizowanej zawartości przy użyciu klawiatury, upewnij się, że elementy zwirtualizowane lub sam kontener przewijania pozwalają na ustawienie ostrości. 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 tabindex wartości -1, 0lub innych wartości, zobacz tabindex.

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 data-blazor-virtualize-reserved-height="1100" aria-hidden="true"></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 data-blazor-virtualize-reserved-height="3400" aria-hidden="true"></div>
</div>
<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 układzie, a każdy element wypełnia całą szerokość poziomą. W typowych przypadkach Virtualize działa z elementami div. 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 flex-direction ustawionym na column. Upewnij się, że bezpośrednie elementy podrzędne komponentu Virtualize<TItem> nie kurczą się zgodnie z regułami fleksji. 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 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 lub content pseudoelementów.

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 głównego kontenera 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 .NET 7, należy unikać stylowania elementów <html>/<body> z 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 kontenera przewijania, jako alternatywę dla posiadania 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 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 .NET 7, należy unikać stylowania elementów <html>/<body> z 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>

Kontroluj nazwę tagu elementu dystansującego

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:

Zgodność z zasadami zabezpieczeń zawartości (CSP)

Naruszeń zasad CSP unika się, ponieważ komponenty Virtualize:

  • Renderuj obliczony odstęp i wysokość symboli zastępczych jako wartości liczbowe w data-blazor-virtualize-reserved-height atrybutach.
  • W razie potrzeby przedstaw pionowe przesunięcie końcowego elementu odstępu jako wartość liczbową w atrybucie data-blazor-virtualize-loop-breaker-transform, aby ukryć ten element odstępu.

Element JSMutationObserver weryfikuje wartości atrybutów i stosuje je za pośrednictwem modelu obiektów CSS (CSSOM) jako opartego na height pikselach i transform stylach.

Komponent Virtualize renderuje dynamiczne atrybuty śródliniowe style w elementach odstępu, ponieważ wysokości odstępów są obliczane w czasie działania na podstawie pozycji przewijania, liczby elementów i średniego rozmiaru elementu, które zmieniają się przy każdej interakcji przewijania. Aby uniknąć naruszeń zasad CSP, renderuj wysokość CSS w atrybucie data-blazor-virtualize-reserved-height zamiast w atrybucie style, co sprawia, że renderowany komponent jest zgodny z rygorystycznymi konfiguracjami Content Security Policy (CSP).

W poniższym przykładzie wysokość jest ustawiona na 3400 pikseli:

<div data-blazor-virtualize-reserved-height="3400" aria-hidden="true"></div>

Komponent Virtualize renderuje dynamiczne atrybuty śródliniowe style w elementach odstępu, ponieważ wysokości odstępów są obliczane w czasie działania na podstawie pozycji przewijania, liczby elementów i średniego rozmiaru elementu, które zmieniają się przy każdej interakcji przewijania. Aplikacje muszą złagodzić style-src za pomocą 'unsafe-inline', aby zezwolić na style inline, dzięki którym składnik będzie działać.