ASP.NET Core Razor komponentvirtualisering
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .
Viktigt!
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .
Den här artikeln beskriver hur du använder komponentvirtualisering i ASP.NET Core Blazor-appar.
Virtualisering
Förbättra den upplevda prestandan för komponentrendering med hjälp av Blazor ramverkets inbyggda virtualiseringsstöd med Virtualize<TItem> komponenten. Virtualisering är en teknik för att begränsa UI-återgivning till bara de delar som för närvarande är synliga. Virtualisering är till exempel användbart när appen måste återge en lång lista med objekt och endast en delmängd av objekten måste vara synliga vid en viss tidpunkt.
Använd komponenten Virtualize<TItem> när:
- Återge en uppsättning dataobjekt i en loop.
- De flesta objekten visas inte på grund av rullning.
- De renderade objekten har samma storlek.
När användaren rullar till en godtycklig punkt i Virtualize<TItem> komponentens lista över objekt beräknar komponenten de synliga objekt som ska visas. Osynliga element visas inte.
Utan virtualisering kan en typisk lista använda en C#-foreach
-loop för att återge varje objekt i en lista. I följande exempel:
-
allFlights
är en samling flygplansflygningar. - Komponenten
FlightSummary
visar information om varje flygning. - Attributet för
@key
-direktiv bevarar varjeFlightSummary
komponents relation till sin renderade flygning genom flygresansFlightId
.
<div style="height:500px;overflow-y:scroll">
@foreach (var flight in allFlights)
{
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
}
</div>
Om samlingen innehåller tusentals flygningar tar det lång tid att återge flygningarna och användarna upplever en märkbar UI-fördröjning. De flesta av flygningarna faller utanför höjden på <div>
-elementet, så de flesta av dem ses inte.
Ersätt foreach
-loopen i föregående exempel med komponenten Virtualize<TItem> i stället för att återge hela listan över flygningar samtidigt:
Ange
allFlights
som en fast objektkälla för att Virtualize<TItem>.Items. Endast de för närvarande synliga flygningarna återges av komponenten Virtualize<TItem>.Om en icke-generisk samling tillhandahåller objekten, till exempel en samling DataRow, följer du anvisningarna i avsnittet Item provider delegate för att ange objekten.
Ange en kontext för varje flygning med parametern
Context
. I följande exempel användsflight
som kontext, vilket ger åtkomst till varje flygs medlemmar.
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights" Context="flight">
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
</Virtualize>
</div>
Om en kontext inte anges med parametern Context
använder du värdet för context
i objektinnehållsmallen för att få åtkomst till varje flygs medlemmar:
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights">
<FlightSummary @key="context.FlightId" Details="@context.Summary" />
</Virtualize>
</div>
Komponenten Virtualize<TItem>:
- Beräknar antalet objekt som ska återges baserat på containerns höjd och storleken på de renderade objekten.
- Beräknar om och renderar om objekten när användaren rullar.
- Hämtar bara sektorn med poster från ett externt API som motsvarar den aktuella synliga regionen, inklusive överskanning, när
ItemsProvider
används i stället förItems
(se avsnittet Item provider delegate).
Objektinnehållet för den Virtualize<TItem> komponenten kan innehålla:
- Vanlig HTML- och Razor kod, som i föregående exempel visas.
- En eller flera Razor komponenter.
- En blandning av HTML/Razor- och Razor-komponenter.
Objektleverantör-delegat
Om du inte vill läsa in alla objekt i minnet eller om samlingen inte är en allmän ICollection<T>kan du ange en metod för att delegera objektprovidern till komponentens Virtualize<TItem>.ItemsProvider parameter som asynkront hämtar de begärda objekten på begäran. I följande exempel innehåller metoden LoadEmployees
objekten till komponenten Virtualize<TItem>:
<Virtualize Context="employee" ItemsProvider="LoadEmployees">
<p>
@employee.FirstName @employee.LastName has the
job title of @employee.JobTitle.
</p>
</Virtualize>
Objektprovidern tar emot en ItemsProviderRequest, som anger det antal objekt som krävs som börjar vid ett specifikt startindex. Objektprovidern hämtar sedan de begärda objekten från en databas eller annan tjänst och returnerar dem som en ItemsProviderResult<TItem> tillsammans med antalet av de totala objekten. Objektprovidern kan välja att hämta objekten med varje begäran eller cachelagrat dem så att de är lättillgängliga.
En Virtualize<TItem> komponent kan bara acceptera en objektkälla från dess parametrar, så försök inte att samtidigt använda en objektprovider och tilldela en samling till Items
. Om båda tilldelas kastas en InvalidOperationException när komponentens parametrar anges under körning.
I följande exempel läses anställda in från en EmployeeService
(visas inte). Det totalEmployees
fältet tilldelas vanligtvis genom att anropa en metod för samma tjänst (till exempel EmployeesService.GetEmployeesCountAsync
) någon annanstans, till exempel under komponentinitiering.
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);
}
I följande exempel är en samling DataRow en icke-generisk samling, så ett ombud för objektprovidern används för virtualisering:
<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 instruerar komponenten att hämta data igen från ItemsProvider. Detta är användbart när externa data ändras. Du behöver vanligtvis inte anropa RefreshDataAsync när du använder Items.
RefreshDataAsync uppdaterar en Virtualize<TItem> komponentens data utan att orsaka en omrendering. Om RefreshDataAsync anropas från en Blazor händelsehanterare eller komponentlivscykelmetod krävs inte utlösande av en rendering eftersom en återgivning utlöses automatiskt i slutet av händelsehanteraren eller livscykelmetoden. Om RefreshDataAsync utlöses separat från en bakgrundsaktivitet eller händelse, till exempel i följande ForecastUpdated
delegering, anropar du StateHasChanged för att uppdatera användargränssnittet i slutet av bakgrundsaktiviteten eller händelsen.
<Virtualize ... @ref="virtualizeComponent">
...
</Virtualize>
...
private Virtualize<FetchData>? virtualizeComponent;
protected override void OnInitialized()
{
WeatherForecastSource.ForecastUpdated += async () =>
{
await InvokeAsync(async () =>
{
await virtualizeComponent?.RefreshDataAsync();
StateHasChanged();
});
});
}
I föregående exempel:
- RefreshDataAsync anropas först för att hämta nya data för komponenten Virtualize<TItem>.
-
StateHasChanged
anropas för att rendera om komponenten.
Platshållare
Eftersom det kan ta lite tid att begära objekt från en fjärrdatakälla kan du rendera en platshållare med objektinnehåll:
- Använd en Placeholder (
<Placeholder>...</Placeholder>
) för att visa innehåll tills objektdata är tillgängliga. - Använd Virtualize<TItem>.ItemContent för att ange objektmallen för listan.
<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>
Tomt innehåll
Använd parametern EmptyContent för att ange innehåll när komponenten har lästs in och antingen Items är tom eller ItemsProviderResult<TItem>.TotalItemCount är noll.
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 ??= [];
}
Ändra OnInitialized
-metoden lambda för att se komponentens visningssträngar:
protected override void OnInitialized() =>
stringList ??= [ "Here's a string!", "Here's another string!" ];
Objektstorlek
Höjden på varje objekt i bildpunkter kan anges med Virtualize<TItem>.ItemSize (standard: 50). I följande exempel ändras höjden på varje objekt från standardvärdet 50 bildpunkter till 25 bildpunkter:
<Virtualize Context="employee" Items="employees" ItemSize="25">
...
</Virtualize>
Komponenten Virtualize<TItem> mäter återgivningsstorleken (höjden) för enskilda objekt efter den inledande återgivningen inträffar. Använd ItemSize för att ange en exakt objektstorlek i förväg för att hjälpa till med korrekt initial återgivningsprestanda och för att säkerställa rätt rullningsposition för sidinläsningar. Om standardvärdet ItemSize gör att vissa objekt återges utanför den synliga vyn utlöses en andra rerender. För att webbläsarens rullningsposition ska behållas korrekt i en virtualiserad lista måste den första återgivningen vara korrekt. Annars kan användarna visa fel objekt.
Antal överskanningar
Virtualize<TItem>.OverscanCount avgör hur många ytterligare objekt som återges före och efter den synliga regionen. Den här inställningen hjälper till att minska återgivningsfrekvensen under rullning. Högre värden resulterar dock i fler element som återges på sidan (standard: 3). I följande exempel ändras antalet overscan från standardvärdet för tre objekt till fyra objekt:
<Virtualize Context="employee" Items="employees" OverscanCount="4">
...
</Virtualize>
Statusändringar
När du gör ändringar i objekt som återges av komponenten Virtualize<TItem> anropar du StateHasChanged för att ange omutvärdering och omrendering av komponenten. För mer information, se ASP.NET Core Razor komponentåtergivning.
Stöd för tangentbordsrullning
Om du vill tillåta användare att bläddra i virtualiserat innehåll med hjälp av tangentbordet kontrollerar du att de virtualiserade elementen eller själva rullningscontainern är fokuserbara. Om du inte tar det här steget fungerar inte tangentbordsrullning i Chromium-baserade webbläsare.
Du kan till exempel använda ett tabindex
-attribut i rullningscontainern:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights">
<div class="flight-info">...</div>
</Virtualize>
</div>
Mer information om innebörden av tabindex
värde -1
, 0
eller andra värden finns i tabindex
.
Avancerade format och rullningsidentifiering
Komponenten Virtualize<TItem> är endast utformad för att stödja specifika elementlayoutmekanismer. För att förstå vilka elementlayouter som fungerar korrekt förklarar följande hur Virtualize
identifierar vilka element som ska visas på rätt plats.
Om källkoden ser ut så här:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights" ItemSize="100">
<div class="flight-info">Flight @context.Id</div>
</Virtualize>
</div>
Vid körning renderar komponenten Virtualize<TItem> en DOM-struktur som liknar följande:
<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>
Det faktiska antalet rader som återges och storleken på mellanslagarna varierar beroende på din formatering och Items
samlingsstorlek. Observera dock, att det finns mellanrums-div
-element som infogats före och efter ditt innehåll. Dessa har två syften:
- Om du vill ange en förskjutning före och efter innehållet, vilket gör att objekt som är synliga för närvarande visas på rätt plats i rullningsintervallet och själva rullningsintervallet representerar den totala storleken på allt innehåll.
- För att identifiera när användaren bläddrar utanför det aktuella synliga intervallet, vilket innebär att olika innehåll måste återges.
Anmärkning
För att lära dig hur du kan styra html-elementtaggen för blanksteg, se avsnittet Kontrollera taggnamnet för blankstegselementet senare i den här artikeln.
Spacer-elementen använder internt en Intersection Observer- för att ta emot meddelanden när de blir synliga.
Virtualize
är beroende av att ta emot dessa händelser.
Virtualize
fungerar under följande förhållanden:
Alla renderade innehållsobjekt, inklusive platshållarinnehåll, har samma höjd. Detta gör det möjligt att beräkna vilket innehåll som motsvarar en viss rullningsposition utan att först hämta varje dataobjekt och återge data till ett DOM-element.
Både mellanrummen och innehållsraderna visas i en sammanhängande vertikal stapel där varje objekt fyller hela den vågräta bredden. I vanliga användningsfall fungerar
Virtualize
meddiv
element. Om du använder CSS för att skapa en mer avancerad layout bör du tänka på följande krav:- Formatering av rullningscontainer kräver en
display
med något av följande värden:-
block
(standardvärdet för endiv
). -
table-row-group
(standardvärdet för entbody
). -
flex
medflex-direction
inställt påcolumn
. Se till att de omedelbara underordnade Virtualize<TItem> komponenten inte krymper under flexregler. Lägg till exempel till.mycontainer > div { flex-shrink: 0 }
.
-
- Innehållsradsformatering kräver en
display
med något av följande värden:-
block
(standardvärdet för endiv
). -
table-row
(standardvärdet för entr
).
-
- Använd inte CSS för att påverka layouten för avståndselementen. Dividerselementen har värdet
display
block
, utom när den överordnade är en tabellradsgrupp, då är de som standardtable-row
. Försök inte påverka bredden eller höjden på mellanrumselementen, bland annat genom att ge dem en kantlinje ellercontent
-pseudoelement.
- Formatering av rullningscontainer kräver en
Alla metoder som hindrar mellanslag och innehållselement från att återges som en enda lodrät stack, eller gör att innehållsobjekten varierar i höjd, förhindrar att Virtualize<TItem> komponent fungerar korrekt.
Virtualisering på rotnivå
Komponenten Virtualize<TItem> stöder användning av själva dokumentet som rullningsrot, som ett alternativ till att ha något annat element med overflow-y: scroll
. I följande exempel formateras elementen <html>
eller <body>
i en komponent med overflow-y: scroll
:
<HeadContent>
<style>
html, body { overflow-y: scroll }
</style>
</HeadContent>
Komponenten Virtualize<TItem> stöder användning av själva dokumentet som rullningsrot, som ett alternativ till att ha något annat element med overflow-y: scroll
. När du använder dokumentet som rullningsrot bör du undvika att formatera <html>
- eller <body>
-elementen med overflow-y: scroll
eftersom det gör att skärningspunktsövervakare behandlar hela den rullningsbara höjden på sidan som det synliga området, i stället för bara fönstrets visningsfönster.
Du kan återskapa det här problemet genom att skapa en stor virtualiserad lista (till exempel 100 000 objekt) och försöka använda dokumentet som rullningsrot med html { overflow-y: scroll }
på sidans CSS-format. Även om det kan fungera korrekt ibland, försöker webbläsaren återge alla 100 000 objekt minst en gång i början av återgivningen, vilket kan orsaka låsning av webbläsarfliken.
Undvik det här problemet innan du släpper .NET 7 genom att antingen undvika att formatera <html>
/<body>
element med overflow-y: scroll
eller använda en alternativ metod. I följande exempel anges höjden på elementet <html>
till drygt 100% av visningsportens höjd:
<HeadContent>
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
</HeadContent>
Komponenten Virtualize<TItem> stöder användning av själva dokumentet som rullningsrot, som ett alternativ till att ha något annat element med overflow-y: scroll
. När du använder dokumentet som rullningsrot bör du undvika att formatera <html>
- eller <body>
-element med overflow-y: scroll
eftersom det gör att sidans fullständiga rullningsbara höjd behandlas som den synliga regionen, i stället för bara fönstrets visningsport.
Du kan återskapa det här problemet genom att skapa en stor virtualiserad lista (till exempel 100 000 objekt) och försöka använda dokumentet som rullningsrot med html { overflow-y: scroll }
på sidans CSS-format. Även om det kan fungera korrekt ibland, försöker webbläsaren återge alla 100 000 objekt minst en gång i början av återgivningen, vilket kan orsaka låsning av webbläsarfliken.
Undvik det här problemet innan du släpper .NET 7 genom att antingen undvika att formatera <html>
/<body>
element med overflow-y: scroll
eller använda en alternativ metod. I följande exempel anges höjden på elementet <html>
till drygt 100% av visningsportens höjd:
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
Kontrollera namn på mellanrumselementtaggen
Om Virtualize<TItem>-komponenten placeras i ett element som kräver ett specifikt underordnat taggnamn, möjliggör SpacerElement för dig att hämta eller ange namnet på virtualiseringsavståndstaggen. Standardvärdet är div
. I följande exempel återges komponenten Virtualize<TItem> inuti tabellkroppselementet (tbody
), så det lämpliga underordnade elementet för en tabellrad (tr
) blir mellanrum.
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();
}
I föregående exempel används dokumentroten som rullningscontainer, så elementen html
och body
formateras med overflow-y: scroll
. Mer information finns i följande resurser:
ASP.NET Core