ASP.NET Core Razor component-virtualisatie
Notitie
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Belangrijk
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikelvoor de huidige release.
In dit artikel wordt uitgelegd hoe u onderdeelvirtualisatie gebruikt in ASP.NET Core Blazor-apps.
Virtualisatie
Verbeter de waargenomen prestaties van onderdeelrendering met behulp van de ingebouwde virtualisatieondersteuning van het Blazor framework met het Virtualize<TItem>-onderdeel. Virtualisatie is een techniek voor het beperken van ui-rendering tot alleen de onderdelen die momenteel zichtbaar zijn. Virtualisatie is bijvoorbeeld handig wanneer de app een lange lijst met items moet weergeven en alleen een subset van items moet worden weergegeven op een bepaald moment.
Gebruik het Virtualize<TItem>-onderdeel wanneer:
- Een set gegevensitems in een lus weergeven.
- De meeste items zijn niet zichtbaar vanwege schuiven.
- De weergegeven items hebben dezelfde grootte.
Wanneer de gebruiker naar een willekeurig punt in de lijst met items van het Virtualize<TItem>-onderdeel schuift, berekent het onderdeel de zichtbare items die moeten worden weergegeven. Items die niet zichtbaar zijn, worden niet gerenderd.
Zonder virtualisatie kan een typische lijst een C# foreach
lus gebruiken om elk item in een lijst weer te geven. In het volgende voorbeeld:
-
allFlights
is een verzameling vliegtuigvluchten. - In het
FlightSummary
onderdeel worden details over elke vlucht weergegeven. - De richtlijnkenmerk
@key
behoudt de relatie van elkFlightSummary
onderdeel tot de weergegeven vlucht door deFlightId
van de vlucht.
<div style="height:500px;overflow-y:scroll">
@foreach (var flight in allFlights)
{
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
}
</div>
Als de verzameling duizenden vluchten bevat, duurt het lang voordat de vluchten worden weergegeven en ervaren gebruikers een merkbare vertraging in de gebruikersinterface. De meeste vluchten vallen buiten de hoogte van het <div>
-element en zijn daarom niet zichtbaar.
In plaats van de volledige lijst met vluchten tegelijk weer te geven, vervangt u de foreach
lus in het voorgaande voorbeeld door het Virtualize<TItem>-onderdeel:
Geef
allFlights
op als een vaste itembron voor Virtualize<TItem>.Items. Alleen de momenteel zichtbare vluchten worden weergegeven door het Virtualize<TItem> onderdeel.Als een niet-algemene verzameling de items levert, bijvoorbeeld een verzameling van DataRow, volgt u de richtlijnen in de sectie Itemprovider gedelegeerd om de items op te geven.
Geef een context op voor elke flight met de parameter
Context
. In het volgende voorbeeld wordtflight
gebruikt als context, die toegang biedt tot de leden van elke vlucht.
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights" Context="flight">
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
</Virtualize>
</div>
Als een context niet is opgegeven met de parameter Context
, gebruikt u de waarde van context
in de sjabloon voor iteminhoud voor toegang tot de leden van elke flight:
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights">
<FlightSummary @key="context.FlightId" Details="@context.Summary" />
</Virtualize>
</div>
Het Virtualize<TItem>-onderdeel:
- Berekent het aantal items dat moet worden weergegeven op basis van de hoogte van de container en de grootte van de gerenderde items.
- Berekent en rendert de items opnieuw terwijl de gebruiker schuift.
- Haalt alleen het segment records op van een externe API die overeenkomt met de momenteel zichtbare regio, inclusief overscan, wanneer
ItemsProvider
wordt gebruikt in plaats vanItems
(zie de sectie itemprovider delegeren).
De iteminhoud voor het Virtualize<TItem>-onderdeel kan het volgende omvatten:
- Html zonder opmaak en Razor code, zoals in het voorgaande voorbeeld wordt weergegeven.
- Een of meer Razor onderdelen.
- Een combinatie van HTML-/Razor- en Razor-onderdelen.
Vertegenwoordiger van de itemprovider
Als u niet alle items in het geheugen wilt laden of als de verzameling geen algemene ICollection<T>is, kunt u een gemachtigde methode voor de itemsprovider opgeven voor de Virtualize<TItem>.ItemsProvider parameter van het onderdeel waarmee de aangevraagde items op aanvraag asynchroon worden opgehaald. In het volgende voorbeeld bevat de methode LoadEmployees
de items voor het Virtualize<TItem> onderdeel:
<Virtualize Context="employee" ItemsProvider="LoadEmployees">
<p>
@employee.FirstName @employee.LastName has the
job title of @employee.JobTitle.
</p>
</Virtualize>
De itemsprovider ontvangt een ItemsProviderRequest, waarmee het vereiste aantal items wordt opgegeven dat begint bij een specifieke beginindex. De itemsprovider haalt vervolgens de aangevraagde items op uit een database of een andere service en retourneert deze als een ItemsProviderResult<TItem> samen met een telling van het totale aantal items. De provider van items kan ervoor kiezen om de items met elke aanvraag op te halen of in de cache op te slaan, zodat ze direct beschikbaar zijn.
Een Virtualize<TItem>-onderdeel kan slechts één itembron van de parameters accepteren, dus probeer niet tegelijkertijd een itemsprovider te gebruiken en een verzameling toe te wijzen aan Items
. Als beide zijn toegewezen, wordt er een InvalidOperationException gegenereerd wanneer de parameters van het onderdeel tijdens runtime worden ingesteld.
In het volgende voorbeeld worden werknemers uit een EmployeeService
geladen (niet weergegeven). Het totalEmployees
veld wordt doorgaans toegewezen door een methode aan te roepen voor dezelfde service (bijvoorbeeld EmployeesService.GetEmployeesCountAsync
) elders, zoals tijdens de initialisatie van onderdelen.
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);
}
In het volgende voorbeeld is een verzameling van DataRow een niet-algemene verzameling, zodat een gemachtigde van de itemsprovider wordt gebruikt voor virtualisatie:
<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 geeft het onderdeel opdracht om de gegevens van de ItemsProvideropnieuw op te vragen. Dit is handig wanneer externe gegevens worden gewijzigd. Het is meestal niet nodig om RefreshDataAsync aan te roepen wanneer u Itemsgebruikt.
RefreshDataAsync werkt de gegevens van een Virtualize<TItem>-onderdeel bij zonder dat het opnieuw gerenderd wordt. Als RefreshDataAsync wordt aangeroepen vanuit een Blazor gebeurtenishandler of levenscyclusmethode voor onderdelen, is het activeren van een render niet vereist omdat een render automatisch wordt geactiveerd aan het einde van de gebeurtenis-handler of levenscyclusmethode. Als RefreshDataAsync afzonderlijk wordt geactiveerd van een achtergrondtaak of gebeurtenis, zoals in de volgende ForecastUpdated
gedelegeerde, roept u StateHasChanged aan om de gebruikersinterface aan het einde van de achtergrondtaak of gebeurtenis bij te werken:
<Virtualize ... @ref="virtualizeComponent">
...
</Virtualize>
...
private Virtualize<FetchData>? virtualizeComponent;
protected override void OnInitialized()
{
WeatherForecastSource.ForecastUpdated += async () =>
{
await InvokeAsync(async () =>
{
await virtualizeComponent?.RefreshDataAsync();
StateHasChanged();
});
});
}
In het voorgaande voorbeeld:
- RefreshDataAsync wordt eerst aangeroepen om nieuwe gegevens te verkrijgen voor het Virtualize<TItem> onderdeel.
-
StateHasChanged
wordt aangeroepen om de component te rerenderen.
Tijdelijke aanduiding
Omdat het aanvragen van items van een externe gegevensbron enige tijd kan duren, hebt u de mogelijkheid om een tijdelijke aanduiding weer te geven met iteminhoud:
- Gebruik een Placeholder (
<Placeholder>...</Placeholder>
) om inhoud weer te geven totdat de itemgegevens beschikbaar zijn. - Gebruik Virtualize<TItem>.ItemContent om de itemsjabloon voor de lijst in te stellen.
<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>
Lege inhoud
Gebruik de parameter EmptyContent om inhoud op te geven wanneer het onderdeel is geladen en Items leeg is of ItemsProviderResult<TItem>.TotalItemCount nul is.
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 ??= [];
}
Wijzig de OnInitialized
methode lambda om de weergavetekenreeksen van het onderdeel weer te geven:
protected override void OnInitialized() =>
stringList ??= [ "Here's a string!", "Here's another string!" ];
Itemgrootte
De hoogte van elk item in pixels kan worden ingesteld met Virtualize<TItem>.ItemSize (standaard: 50). In het volgende voorbeeld wordt de hoogte van elk item gewijzigd van de standaardwaarde van 50 pixels in 25 pixels:
<Virtualize Context="employee" Items="employees" ItemSize="25">
...
</Virtualize>
Het Virtualize<TItem>-onderdeel meet de weergavegrootte (hoogte) van afzonderlijke items nadat de eerste weergave plaatsvindt. Gebruik ItemSize om vooraf een exacte itemgrootte te bieden om te helpen bij de nauwkeurige prestaties van de eerste weergave en om de juiste schuifpositie voor het opnieuw laden van pagina's te garanderen. Als de standaard-ItemSize ervoor zorgt dat sommige items buiten de momenteel zichtbare weergave worden weergegeven, wordt er een tweede rerender geactiveerd. Als u de schuifpositie van de browser in een gevirtualiseerde lijst correct wilt behouden, moet de eerste weergave juist zijn. Als dat niet het probleem is, kunnen gebruikers de verkeerde items bekijken.
Aantal overscans
Virtualize<TItem>.OverscanCount bepaalt hoeveel extra items worden weergegeven vóór en na het zichtbare gebied. Deze instelling helpt bij het verminderen van de frequentie van rendering tijdens het schuiven. Hogere waarden resulteren echter in meer elementen die op de pagina worden weergegeven (standaard: 3). In het volgende voorbeeld wordt het aantal overscans gewijzigd van de standaardwaarde van drie items in vier items:
<Virtualize Context="employee" Items="employees" OverscanCount="4">
...
</Virtualize>
Toestandswijzigingen
Wanneer wijzigingen worden aangebracht in items die door het Virtualize<TItem> onderdeel worden weergegeven, roep StateHasChanged aan om opnieuw evaluatie en opnieuw renderen van het onderdeel in de wachtrij te zetten. Zie ASP.NET Core Razor component renderingvoor meer informatie.
Ondersteuning voor toetsenbord schuiven
Als u wilt dat gebruikers met hun toetsenbord door gevirtualiseerde inhoud kunnen schuiven, moet u ervoor zorgen dat de gevirtualiseerde elementen of schuifcontainer zelf focusbaar zijn. Als u deze stap niet uitvoert, werkt toetsenbord schuiven niet in browsers op basis van Chromium.
U kunt bijvoorbeeld een tabindex
kenmerk in de schuifcontainer gebruiken:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights">
<div class="flight-info">...</div>
</Virtualize>
</div>
Zie tabindex
(MDN-documentatie)voor meer informatie over de betekenis van tabindex
waarde -1
, 0
of andere waarden.
Geavanceerde stijlen en schuifdetectie
Het Virtualize<TItem> onderdeel is alleen ontworpen ter ondersteuning van specifieke mechanismen voor het indelen van elementen. Als u wilt weten welke elementindelingen correct werken, wordt in het volgende uitgelegd hoe Virtualize
detecteert welke elementen zichtbaar moeten zijn voor weergave op de juiste plaats.
Als uw broncode er als volgt uitziet:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights" ItemSize="100">
<div class="flight-info">Flight @context.Id</div>
</Virtualize>
</div>
Tijdens runtime geeft het Virtualize<TItem>-onderdeel een DOM-structuur weer die vergelijkbaar is met de volgende:
<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>
Het werkelijke aantal weergegeven rijen en de grootte van de afstandhouders variëren afhankelijk van uw stijl en Items
verzamelingsgrootte. U merkt echter op dat er tussenruimte-elementen div
vóór en na uw inhoud zijn geïnjecteerd. Deze dienen twee doeleinden:
- Om een marge voor en na uw inhoud op te geven, zodat momenteel zichtbare items op de juiste locatie in het scrollbereik verschijnen en het scrollbereik zelf de totale omvang van alle inhoud weergeeft.
- Als u wilt detecteren wanneer de gebruiker buiten het huidige zichtbare bereik schuift, betekent dit dat verschillende inhoud moet worden weergegeven.
Notitie
Zie de sectie De tagnaam van het spacer-element verderop in dit artikel voor meer informatie over het beheren van het spacer HTML-elementtag.
De spacer-elementen gebruiken intern een Intersection Observer om een melding te ontvangen wanneer ze zichtbaar worden.
Virtualize
is afhankelijk van het ontvangen van deze gebeurtenissen.
Virtualize
werkt onder de volgende voorwaarden:
Alle weergegeven inhoudsitems, waaronder tijdelijke aanduiding voor inhoud, hebben dezelfde hoogte. Hierdoor kunt u berekenen welke inhoud overeenkomt met een bepaalde schuifpositie zonder eerst elk gegevensitem op te halen en de gegevens weer te geven in een DOM-element.
Zowel de afstandhouders als de inhoudsrijen worden weergegeven in één verticale stapel, waarbij elk item de volledige horizontale breedte vult. In typische gebruiksvoorbeelden werkt
Virtualize
metdiv
elementen. Als u CSS gebruikt om een geavanceerdere indeling te maken, moet u rekening houden met de volgende vereisten:- Scroll container styling vereist een
display
met een van de volgende waarden:-
block
(de standaardinstelling voor eendiv
). -
table-row-group
(de standaardinstelling voor eentbody
). -
flex
metflex-direction
ingesteld opcolumn
. Zorg ervoor dat directe onderliggende elementen van het Virtualize<TItem> onderdeel niet kleiner worden onder flexregels. Voeg bijvoorbeeld.mycontainer > div { flex-shrink: 0 }
toe.
-
- Voor de stijl van inhoudsrijen is een
display
met een van de volgende waarden vereist:-
block
(de standaardinstelling voor eendiv
). -
table-row
(de standaardinstelling voor eentr
).
-
- Gebruik CSS niet om de indeling voor de spacer-elementen te verstoren. De afstandselementen hebben een
display
waarde vanblock
, behalve als het bovenliggende element een tabelrijgroep is, in welk geval ze standaardtable-row
. Probeer niet de breedte of hoogte van het spacer-element te beïnvloeden, ook niet door ervoor te zorgen dat ze een rand ofcontent
pseudo-elementen hebben.
- Scroll container styling vereist een
Elke benadering die verhindert dat de afstandhouders en inhoudselementen als één verticale stapel worden weergegeven, of ervoor zorgt dat de inhoudsitems in hoogte verschillen, voorkomt dat het Virtualize<TItem>-onderdeel correct functioneert.
Virtualisatie op hoofdniveau
Het Virtualize<TItem>-onderdeel ondersteunt het gebruik van het document zelf als de scrollhoofd, als alternatief voor een ander element met overflow-y: scroll
. In het volgende voorbeeld worden de elementen <html>
of <body>
opgemaakt in een onderdeel met overflow-y: scroll
:
<HeadContent>
<style>
html, body { overflow-y: scroll }
</style>
</HeadContent>
Het Virtualize<TItem>-onderdeel ondersteunt het gebruik van het document zelf als de scrollwortel, als alternatief voor een ander element met overflow-y: scroll
. Wanneer u het document gebruikt als scrollroot, vermijd dan het stylen van de <html>
- of <body>
-elementen met overflow-y: scroll
, omdat de snijpuntwaarnemer de volledige scrollbare hoogte van de pagina als het zichtbare gebied behandelt, in plaats van alleen de vensterweergave.
Je kunt dit probleem reproduceren door een grote gevirtualiseerde lijst te maken (bijvoorbeeld 100.000 items) en proberen het document te gebruiken als scrolroot met html { overflow-y: scroll }
in de CSS-stijlen van de pagina. Hoewel het soms correct werkt, probeert de browser alle 100.000 items ten minste één keer aan het begin van de rendering weer te geven, wat kan leiden tot een vergrendeling van het browsertabblad.
Als u dit probleem wilt omzeilen vóór de release van .NET 7, vermijdt u styling <html>
/<body>
elementen met overflow-y: scroll
of gebruikt u een alternatieve benadering. In het volgende voorbeeld is de hoogte van het element <html>
ingesteld op iets meer dan 100% van de viewporthoogte:
<HeadContent>
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
</HeadContent>
Het Virtualize<TItem>-onderdeel ondersteunt het gebruik van het document zelf als de scrollbasis, als alternatief voor een ander element met overflow-y: scroll
. Wanneer u het document als scroll-hoofdelement gebruikt, vermijd dan het stylen van de <html>
of <body>
elementen met overflow-y: scroll
, want dat verzorgt ervoor dat de volledige scrollbare hoogte van de pagina wordt behandeld als het zichtbare deel, in plaats van alleen het venster viewport.
U kunt dit probleem reproduceren door een grote gevirtualiseerde lijst te maken (bijvoorbeeld 100.000 items) en proberen het document als scrollbasis te gebruiken met html { overflow-y: scroll }
in de CSS-stijlen van de pagina. Hoewel het soms correct werkt, probeert de browser alle 100.000 items ten minste één keer aan het begin van de rendering weer te geven, wat kan leiden tot een vergrendeling van het browsertabblad.
Als u dit probleem wilt omzeilen vóór de release van .NET 7, vermijdt u styling <html>
/<body>
elementen met overflow-y: scroll
of gebruikt u een alternatieve benadering. In het volgende voorbeeld is de hoogte van het element <html>
ingesteld op iets meer dan 100% van de viewporthoogte:
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
De tagnaam van het spacer-element beheren
Als het Virtualize<TItem>-onderdeel in een element wordt geplaatst waarvoor een specifieke naam van een onderliggende tag vereist is, kunt u met SpacerElement de virtualisatiespacertagnaam ophalen of instellen. De standaardwaarde is div
. In het volgende voorbeeld wordt de Virtualize<TItem>-component weergegeven in een tabellichaamelement (tbody
), zodat het juiste onderliggende element voor een tabelrij (tr
) is ingesteld als de spatiehouder.
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();
}
In het voorgaande voorbeeld wordt de documentroot gebruikt als scrollcontainer, zodat de html
- en body
-elementen worden gestijld met overflow-y: scroll
. Zie de volgende bronnen voor meer informatie: