virtualizace komponent ASP.NET Core Razor

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Tento článek vysvětluje, jak používat virtualizaci komponent v aplikacích ASP.NET Core Blazor .

Virtualizace

Zlepšení vnímaného výkonu vykreslování komponent pomocí Blazor integrované podpory virtualizace architektury s komponentou Virtualize<TItem> . Virtualizace je technika omezení vykreslování uživatelského rozhraní jenom na aktuálně viditelné části. Například virtualizace je užitečná, když aplikace musí vykreslit dlouhý seznam položek a v libovolném okamžiku se vyžaduje jenom podmnožina položek.

Komponentu použijte v následujících Virtualize<TItem> případech:

  • Vykreslení sady datových položek ve smyčce
  • Většina položek není viditelná kvůli posouvání.
  • Vykreslené položky mají stejnou velikost.

Když se uživatel v seznamu položek komponenty posune na libovolný bod Virtualize<TItem> , komponenta vypočítá viditelné položky, které se mají zobrazit. Nezoznané položky se nevykreslují.

Bez virtualizace může typický seznam použít smyčku jazyka C# foreach k vykreslení každé položky v seznamu. V následujícím příkladu:

  • allFlights je kolekce letadel.
  • Komponenta FlightSummary zobrazuje podrobnosti o jednotlivých testovacích verzích.
  • Atribut @key direktivy zachovává vztah každé FlightSummary komponenty k jeho vykreslené letu let FlightIdlet .
<div style="height:500px;overflow-y:scroll">
    @foreach (var flight in allFlights)
    {
        <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
    }
</div>

Pokud kolekce obsahuje tisíce letů, vykreslování letů trvá dlouhou dobu a uživatelé zaznamenat znatelnou prodlevu uživatelského rozhraní. Většina letů není vidět, protože spadají mimo výšku <div> prvku.

Místo vykreslení celého seznamu letů najednou nahraďte foreach smyčku v předchozím příkladu komponentou Virtualize<TItem> :

  • Zadejte allFlights jako zdroj pevné položky do Virtualize<TItem>.Items. Komponenta Virtualize<TItem> vykresluje pouze aktuálně viditelné lety.

    Pokud kolekce, která není obecná, poskytuje položky, například kolekci DataRow, postupujte podle pokynů v oddílu delegáta zprostředkovatele položky a zadejte položky.

  • Zadejte kontext pro každou testovací verzi s parametrem Context . V následujícím příkladu flight se používá jako kontext, který poskytuje přístup k členům každé testovací verze.

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

Pokud není zadaný kontext s parametrem Context , použijte hodnotu context v šabloně obsahu položky pro přístup ke členům jednotlivých testovacích verzí:

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

Komponenta Virtualize<TItem> :

  • Vypočítá počet položek, které se mají vykreslit na základě výšky kontejneru a velikosti vykreslovaných položek.
  • Přepočítá a znovu vygeneruje položky při posouvání uživatelem.
  • Načte pouze řez záznamů z externího rozhraní API, které odpovídá aktuálně viditelné oblasti, včetně přeskenu, při ItemsProvider použití místo Items (viz oddíl delegáta zprostředkovatele položky).

Obsah položky pro komponentu Virtualize<TItem> může zahrnovat:

  • Prostý kód HTML a Razor kód, jak ukazuje předchozí příklad.
  • Jedna nebo více Razor součástí.
  • Kombinace HTML/Razor a Razor komponent.

Delegát zprostředkovatele položek

Pokud nechcete načíst všechny položky do paměti nebo kolekce není obecná ICollection<T>, můžete zadat metodu delegáta zprostředkovatele položek na parametr komponenty Virtualize<TItem>.ItemsProvider , který asynchronně načte požadované položky na vyžádání. V následujícím příkladu LoadEmployees metoda poskytuje položky komponentě Virtualize<TItem> :

<Virtualize Context="employee" ItemsProvider="LoadEmployees">
    <p>
        @employee.FirstName @employee.LastName has the 
        job title of @employee.JobTitle.
    </p>
</Virtualize>

Zprostředkovatel položek obdrží hodnotu ItemsProviderRequest, která určuje požadovaný počet položek počínaje konkrétním počátečním indexem. Zprostředkovatel položek pak načte požadované položky z databáze nebo jiné služby a vrátí je jako ItemsProviderResult<TItem> počet celkových položek. Poskytovatel položek se může rozhodnout načíst položky s jednotlivými požadavky nebo je uložit do mezipaměti, aby byly snadno dostupné.

Komponenta Virtualize<TItem> může přijímat pouze jeden zdroj položek ze svých parametrů, takže se nepokoušejte současně používat zprostředkovatele položek a přiřazovat kolekci Items. Pokud jsou oba přiřazeny, vyvolá se při InvalidOperationException nastavení parametrů komponenty za běhu.

Následující příklad načte zaměstnance z objektu EmployeeService (není zobrazeno):

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);
}

V následujícím příkladu je kolekce DataRow ne generické kolekce, takže delegát zprostředkovatele položek se používá pro virtualizaci:

<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 dává komponentě pokyn, aby znovu požadovala data z jeho ItemsProvider. To je užitečné, když se změní externí data. Při použití Itemsnení obvykle nutné volat RefreshDataAsync .

RefreshDataAsyncVirtualize<TItem> aktualizuje data komponenty, aniž by to způsobilo opětovné obnovení. Pokud RefreshDataAsync je vyvolána z Blazor obslužné rutiny události nebo metody životního cyklu komponent, aktivace vykreslení není vyžadována, protože vykreslení se automaticky aktivuje na konci obslužné rutiny události nebo metody životního cyklu. Pokud RefreshDataAsync se aktivuje odděleně od úlohy nebo události na pozadí, například v následujícím ForecastUpdated delegátu, volání StateHasChanged pro aktualizaci uživatelského rozhraní na konci úlohy nebo události na pozadí:

<Virtualize ... @ref="virtualizeComponent">
    ...
</Virtualize>

...

private Virtualize<FetchData>? virtualizeComponent;

protected override void OnInitialized()
{
    WeatherForecastSource.ForecastUpdated += async () => 
    {
        await InvokeAsync(async () =>
        {
            await virtualizeComponent?.RefreshDataAsync();
            StateHasChanged();
        });
    });
}

V předchozím příkladu:

  • RefreshDataAsync je volána jako první pro získání nových dat pro komponentu Virtualize<TItem> .
  • StateHasChanged je volána k opětovnému vyřazuje komponentu.

Zástupný symbol

Vzhledem k tomu, že vyžádání položek ze vzdáleného zdroje dat může nějakou dobu trvat, máte možnost vykreslit zástupný symbol s obsahem položky:

<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>

Prázdný obsah

Pomocí parametru EmptyContent můžete zadat obsah, pokud je komponenta načtena a Items je prázdná nebo ItemsProviderResult<TItem>.TotalItemCount je nula.

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();
}

Změňte metodu OnInitialized lambda tak, aby se zobrazily řetězce zobrazení komponenty:

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

Velikost položky

Výšku každé položky v pixelech lze nastavit pomocí Virtualize<TItem>.ItemSize (výchozí hodnota: 50). Následující příklad změní výšku každé položky z výchozích 50 pixelů na 25 pixelů:

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

Ve výchozím nastavení Virtualize<TItem> měří komponenta velikost vykreslování (výšku) jednotlivých položek po počátečním vykreslení. Umožňuje ItemSize předem poskytnout přesnou velikost položky, která vám pomůže s přesným počátečním výkonem vykreslení a zajistit správnou pozici posouvání pro opětovné načtení stránky. Pokud výchozí nastavení ItemSize způsobí, že se některé položky vykreslují mimo aktuálně viditelné zobrazení, aktivuje se druhé opětovné vykreslení. Aby bylo možné správně udržovat pozici posouvání prohlížeče ve virtualizovaném seznamu, musí být počáteční vykreslení správné. Pokud ne, uživatelé můžou zobrazit nesprávné položky.

Overscan count

Virtualize<TItem>.OverscanCount určuje, kolik dalších položek se vykresluje před a za viditelnou oblastí. Toto nastavení pomáhá snížit frekvenci vykreslování během posouvání. Vyšší hodnoty ale mají za následek více prvků vykreslených na stránce (výchozí hodnota: 3). Následující příklad změní přehledat počet z výchozích tří položek na čtyři položky:

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

Změny stavu

Při provádění změn položek vykreslovaných komponentou Virtualize<TItem> volejte StateHasChanged vynucení opětovného vyhodnocení a opětovného vykreslení komponenty. Další informace najdete v tématu Vykreslování komponent ASP.NET Core Razor.

Podpora posouvání pomocí klávesnice

Pokud chcete uživatelům umožnit posouvání virtualizovaného obsahu pomocí klávesnice, ujistěte se, že jsou virtualizované prvky nebo samotný kontejner posouvání fokus. Pokud tento krok neuděláte, posouvání pomocí klávesnice nefunguje v prohlížečích založených na Chromiu.

Můžete například použít tabindex atribut v kontejneru scroll:

<div style="height:500px; overflow-y:scroll" tabindex="-1">
    <Virtualize Items="allFlights">
        <div class="flight-info">...</div>
    </Virtualize>
</div>

Další informace o významu hodnoty , nebo jiných hodnotách najdetetabindexv dokumentaci k MDN. 0-1tabindex

Pokročilé styly a detekce posouvání

Komponenta Virtualize<TItem> je určena pouze pro podporu konkrétních mechanismů rozložení prvků. Abyste pochopili, která rozložení prvků fungují správně, vysvětluje následující část, jak Virtualize zjistí, které prvky by měly být viditelné pro zobrazení na správném místě.

Pokud zdrojový kód vypadá takto:

<div style="height:500px; overflow-y:scroll" tabindex="-1">
    <Virtualize Items="allFlights" ItemSize="100">
        <div class="flight-info">Flight @context.Id</div>
    </Virtualize>
</div>

Komponenta Virtualize<TItem> za běhu vykreslí strukturu DOM podobnou následující:

<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>

Skutečný počet vykreslených řádků a velikost mezerník se liší podle stylu a Items velikosti kolekce. Všimněte si však, že před obsahem a za obsah se vloží mezerník div . Slouží ke dvěma účelům:

  • Pokud chcete poskytnout posun před a za obsahem, což způsobí, že se aktuálně viditelné položky zobrazí ve správném umístění v oblasti posouvání a samotné oblasti posouvání představující celkovou velikost veškerého obsahu.
  • Pokud chcete zjistit, kdy se uživatel posunuje nad aktuální viditelnou oblast, což znamená, že se musí vykreslit jiný obsah.

Poznámka:

Informace o tom, jak řídit značku elementu HTML mezerníku, najdete v části Ovládací prvek název značky elementu mezerníku dále v tomto článku.

Prvky mezerník interně používají pozorovatel průniku k příjmu oznámení, když se stanou viditelnými. Virtualize závisí na příjmu těchto událostí.

Virtualize funguje za následujících podmínek:

  • Všechny vykreslené položky obsahu, včetně zástupného obsahu, mají stejnou výšku. To umožňuje vypočítat, který obsah odpovídá dané pozici posouvání, aniž byste nejdřív načítali každou datovou položku a vykreslili data do prvku MODELU DOM.

  • Řádky mezerníku i obsahu se vykreslují v jednom svislém zásobníku s každou položkou, která vyplňuje celou vodorovnou šířku. Toto je obecně výchozí hodnota. V typických případech s div prvky Virtualize funguje ve výchozím nastavení. Pokud k vytvoření pokročilejšího rozložení používáte šablony stylů CSS, mějte na paměti následující požadavky:

    • Stylování kontejneru posouvání vyžaduje některou display z následujících hodnot:
      • block (výchozí hodnota pro a div).
      • table-row-group (výchozí hodnota pro a tbody).
      • flex s flex-direction nastavenou na columnhodnotu . Zajistěte, aby se okamžité podřízené položky Virtualize<TItem> komponenty nezmenšily v rámci pravidel flex. Přidejte například .mycontainer > div { flex-shrink: 0 }.
    • Stylování řádků obsahu vyžaduje display jednu z následujících hodnot:
      • block (výchozí hodnota pro a div).
      • table-row (výchozí hodnota pro a tr).
    • Nepoužívejte šablony stylů CSS k narušení rozložení prvků mezerník. Ve výchozím nastavení mají display elementy mezery hodnotu block, s výjimkou případů, kdy nadřazený je skupina řádků tabulky, v takovém případě jsou výchozí table-row. Nepokoušejte se ovlivnit šířku nebo výšku prvku mezerníku, včetně jejich příčin, že mají ohraničení nebo content pseudo-prvky.

Jakýkoli přístup, který zastaví vykreslování prvků mezer a obsahu jako jednoho svislého zásobníku nebo způsobí, že se položky obsahu budou lišit ve výšce, brání správnému Virtualize<TItem> fungování komponenty.

Virtualizace na kořenové úrovni

Komponenta Virtualize<TItem> podporuje použití samotného dokumentu jako kořen posouvání, jako alternativu k tomu, že má nějaký jiný prvek s overflow-y: scroll. V následujícím příkladu <html><body> jsou prvky stylovány v komponentě s overflow-y: scroll:

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

Komponenta Virtualize<TItem> podporuje použití samotného dokumentu jako kořen posouvání, jako alternativu k tomu, že má nějaký jiný prvek s overflow-y: scroll. Pokud dokument používáte jako kořen posouvání, vyhněte se stylům <html> nebo prvků, overflow-y: scroll protože pozorovatel průsečíku zachází s úplnou výškou stránky jako s viditelnou oblastí, a ne jenom s <body> oblastí zobrazení okna.

Tento problém můžete reprodukovat vytvořením velkého virtualizovaného seznamu (například 100 000 položek) a pokusem o použití dokumentu jako kořenového adresáře html { overflow-y: scroll } ve stylech CSS stránky. I když může někdy správně fungovat, prohlížeč se pokusí vykreslit všech 100 000 položek alespoň jednou při spuštění vykreslování, což může způsobit uzamčení karty prohlížeče.

Chcete-li tento problém vyřešit před vydáním rozhraní .NET 7, vyhněte se stylům <html>/<body> prvků s overflow-y: scroll alternativním přístupem nebo použijte alternativní přístup. V následujícím příkladu je výška <html> prvku nastavená na více než 100 % výšky oblasti zobrazení:

<HeadContent>
    <style>
        html { min-height: calc(100vh + 0.3px) }
    </style>
</HeadContent>

Komponenta Virtualize<TItem> podporuje použití samotného dokumentu jako kořen posouvání, jako alternativu k tomu, že má nějaký jiný prvek s overflow-y: scroll. Pokud dokument používáte jako kořen posuvníku, vyhněte se stylům <html> nebo <body> prvků, overflow-y: scroll protože může způsobit, že úplná výška stránky bude považována za viditelnou oblast, a ne jen jako oblast zobrazení okna.

Tento problém můžete reprodukovat vytvořením velkého virtualizovaného seznamu (například 100 000 položek) a pokusem o použití dokumentu jako kořenového adresáře html { overflow-y: scroll } ve stylech CSS stránky. I když může někdy správně fungovat, prohlížeč se pokusí vykreslit všech 100 000 položek alespoň jednou při spuštění vykreslování, což může způsobit uzamčení karty prohlížeče.

Chcete-li tento problém vyřešit před vydáním rozhraní .NET 7, vyhněte se stylům <html>/<body> prvků s overflow-y: scroll alternativním přístupem nebo použijte alternativní přístup. V následujícím příkladu je výška <html> prvku nastavená na více než 100 % výšky oblasti zobrazení:

<style>
    html { min-height: calc(100vh + 0.3px) }
</style>

Řízení názvu značky elementu mezerníku

Pokud je komponenta Virtualize<TItem> umístěna uvnitř elementu, který vyžaduje konkrétní název podřízené značky, SpacerElement umožňuje získat nebo nastavit název značky mezerníku virtualizace. Výchozí hodnota je div. V následujícím příkladu se komponenta Virtualize<TItem> vykresluje uvnitř elementu těla tabulky (tbody), takže příslušný podřízený prvek pro řádek tabulky (tr) je nastaven jako mezerník.

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();
}

V předchozím příkladu se kořen dokumentu používá jako kontejner posouvání, takže html prvky jsou body stylovány pomocí overflow-y: scroll. Další informace naleznete v následujících zdrojích: