virtualisasi komponen ASP.NET Core Razor
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Peringatan
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Penting
Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.
Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Artikel ini menjelaskan cara menggunakan virtualisasi komponen di aplikasi ASP.NET Core Blazor .
Virtualization
Tingkatkan performa rendering komponen yang dirasakan menggunakan Blazor dukungan virtualisasi bawaan kerangka kerja dengan Virtualize<TItem> komponen . Virtualisasi adalah teknik untuk membatasi penyajian UI hanya pada bagian yang saat ini terlihat. Misalnya, virtualisasi sangat membantu ketika aplikasi harus merender daftar panjang item dan hanya subset item yang diperlukan untuk terlihat pada waktu tertentu.
Virtualize<TItem> Gunakan komponen saat:
- Merender sekumpulan item data dalam perulangan.
- Sebagian besar item tidak terlihat karena pengguliran.
- Item yang dirender berukuran sama.
Saat pengguna menggulir ke titik arbitrer dalam Virtualize<TItem> daftar item komponen, komponen menghitung item yang terlihat untuk ditampilkan. Item yang tidak dilihat tidak dirender.
Tanpa virtualisasi, daftar umum mungkin menggunakan perulangan C# foreach
untuk merender setiap item dalam daftar. Dalam contoh berikut:
allFlights
adalah kumpulan penerbangan pesawat terbang.- Komponen
FlightSummary
menampilkan detail tentang setiap penerbangan. - Atribut
@key
direktif mempertahankan hubungan setiapFlightSummary
komponen ke penerbangan yang dirender oleh penerbanganFlightId
.
<div style="height:500px;overflow-y:scroll">
@foreach (var flight in allFlights)
{
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
}
</div>
Jika koleksi berisi ribuan penerbangan, penyajian penerbangan membutuhkan waktu lama dan pengguna mengalami jeda UI yang nyata. Sebagian besar penerbangan jatuh di luar ketinggian <div>
elemen, jadi sebagian besar dari mereka tidak terlihat.
Alih-alih merender seluruh daftar penerbangan sekaligus, ganti foreach
perulangan dalam contoh sebelumnya dengan Virtualize<TItem> komponen:
Tentukan
allFlights
sebagai sumber item tetap ke Virtualize<TItem>.Items. Hanya penerbangan yang terlihat saat ini yang dirender oleh Virtualize<TItem> komponen.Jika koleksi non-generik menyediakan item, misalnya kumpulan DataRow, ikuti panduan di bagian Delegasi penyedia item untuk menyediakan item.
Tentukan konteks untuk setiap penerbangan dengan
Context
parameter . Dalam contoh berikut,flight
digunakan sebagai konteks, yang menyediakan akses ke setiap anggota penerbangan.
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights" Context="flight">
<FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
</Virtualize>
</div>
Jika konteks tidak ditentukan dengan Context
parameter , gunakan nilai context
dalam templat konten item untuk mengakses setiap anggota penerbangan:
<div style="height:500px;overflow-y:scroll">
<Virtualize Items="allFlights">
<FlightSummary @key="context.FlightId" Details="@context.Summary" />
</Virtualize>
</div>
Komponen Virtualize<TItem> :
- Menghitung jumlah item yang akan dirender berdasarkan tinggi kontainer dan ukuran item yang dirender.
- Menghitung ulang dan merender item saat pengguna menggulir.
- Hanya mengambil ikatan rekaman dari API eksternal yang sesuai dengan wilayah yang saat ini terlihat, termasuk overscan, saat
ItemsProvider
digunakan alih-alihItems
(lihat bagian Delegasi penyedia item).
Konten item untuk Virtualize<TItem> komponen dapat mencakup:
- HTML dan Razor kode biasa, seperti yang ditunjukkan contoh sebelumnya.
- Satu atau beberapa Razor komponen.
- Campuran HTML/Razor dan Razor komponen.
Delegasi penyedia item
Jika Anda tidak ingin memuat semua item ke dalam memori atau koleksi bukan generik ICollection<T>, Anda dapat menentukan metode delegasi penyedia item ke parameter komponen Virtualize<TItem>.ItemsProvider yang secara asinkron mengambil item yang diminta sesuai permintaan. Dalam contoh berikut, LoadEmployees
metode ini menyediakan item ke Virtualize<TItem> komponen:
<Virtualize Context="employee" ItemsProvider="LoadEmployees">
<p>
@employee.FirstName @employee.LastName has the
job title of @employee.JobTitle.
</p>
</Virtualize>
Penyedia item menerima ItemsProviderRequest, yang menentukan jumlah item yang diperlukan mulai dari indeks mulai tertentu. Penyedia item kemudian mengambil item yang diminta dari database atau layanan lain dan mengembalikannya sebagai ItemsProviderResult<TItem> bersama dengan jumlah total item. Penyedia item dapat memilih untuk mengambil item dengan setiap permintaan atau menyimpannya dalam cache sehingga tersedia dengan mudah.
Komponen Virtualize<TItem> hanya dapat menerima satu sumber item dari parameternya, jadi jangan mencoba menggunakan penyedia item secara bersamaan dan menetapkan koleksi ke Items
. Jika keduanya ditetapkan, akan InvalidOperationException dilemparkan saat parameter komponen diatur pada runtime.
Contoh berikut memuat karyawan dari EmployeeService
(tidak ditampilkan):
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);
}
Dalam contoh berikut, kumpulan DataRow adalah koleksi non-generik, sehingga delegasi penyedia item digunakan untuk virtualisasi:
<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 menginstruksikan komponen untuk me-rerequest data dari ItemsProvider. Ini berguna ketika data eksternal berubah. Biasanya tidak perlu memanggil RefreshDataAsync saat menggunakan Items.
RefreshDataAsyncVirtualize<TItem> memperbarui data komponen tanpa menyebabkan rerender. Jika RefreshDataAsync dipanggil dari Blazor penanganan aktivitas atau metode siklus hidup komponen, memicu render tidak diperlukan karena render secara otomatis dipicu di akhir metode penanganan aktivitas atau siklus hidup. Jika RefreshDataAsync dipicu secara terpisah dari tugas atau peristiwa latar belakang, seperti di delegasi berikut ForecastUpdated
, panggil StateHasChanged untuk memperbarui UI di akhir tugas atau peristiwa latar belakang:
<Virtualize ... @ref="virtualizeComponent">
...
</Virtualize>
...
private Virtualize<FetchData>? virtualizeComponent;
protected override void OnInitialized()
{
WeatherForecastSource.ForecastUpdated += async () =>
{
await InvokeAsync(async () =>
{
await virtualizeComponent?.RefreshDataAsync();
StateHasChanged();
});
});
}
Dalam contoh sebelumnya:
- RefreshDataAsync dipanggil terlebih dahulu untuk mendapatkan data baru untuk Virtualize<TItem> komponen.
StateHasChanged
dipanggil untuk merender komponen.
Placeholder
Karena meminta item dari sumber data jarak jauh mungkin memakan waktu, Anda memiliki opsi untuk merender tempat penampung dengan konten item:
- Placeholder Gunakan (
<Placeholder>...</Placeholder>
) untuk menampilkan konten hingga data item tersedia. - Gunakan Virtualize<TItem>.ItemContent untuk mengatur templat item untuk daftar.
<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>
Isi kosong
EmptyContent Gunakan parameter untuk menyediakan konten ketika komponen telah dimuat dan Items kosong atau ItemsProviderResult<TItem>.TotalItemCount nol.
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();
}
@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();
}
OnInitialized
Ubah metode lambda untuk melihat string tampilan komponen:
protected override void OnInitialized() =>
stringList ??= new() { "Here's a string!", "Here's another string!" };
Ukuran item
Tinggi setiap item dalam piksel dapat diatur dengan Virtualize<TItem>.ItemSize (default: 50). Contoh berikut mengubah tinggi setiap item dari default 50 piksel menjadi 25 piksel:
<Virtualize Context="employee" Items="employees" ItemSize="25">
...
</Virtualize>
Komponen Virtualize<TItem> mengukur ukuran penyajian (tinggi) item individual setelah render awal terjadi. Gunakan ItemSize untuk memberikan ukuran item yang tepat terlebih dahulu untuk membantu kinerja render awal yang akurat dan untuk memastikan posisi gulir yang benar untuk pemuatan ulang halaman. Jika default ItemSize menyebabkan beberapa item dirender di luar tampilan yang saat ini terlihat, rerender kedua dipicu. Untuk mempertahankan posisi gulir browser dengan benar dalam daftar virtual, render awal harus benar. Jika tidak, pengguna mungkin melihat item yang salah.
Jumlah overscan
Virtualize<TItem>.OverscanCount menentukan berapa banyak item tambahan yang dirender sebelum dan sesudah wilayah yang terlihat. Pengaturan ini membantu mengurangi frekuensi penyajian selama pengguliran. Namun, nilai yang lebih tinggi menghasilkan lebih banyak elemen yang dirender di halaman (default: 3). Contoh berikut mengubah jumlah overscan dari default tiga item menjadi empat item:
<Virtualize Context="employee" Items="employees" OverscanCount="4">
...
</Virtualize>
Status Perubahan
Saat membuat perubahan pada item yang dirender oleh Virtualize<TItem> komponen, panggil StateHasChanged untuk mengantre evaluasi ulang dan penyajian ulang komponen. Untuk informasi lebih lanjut, lihat perenderan komponen Razor ASP.NET Core.
Dukungan gulir keyboard
Untuk memungkinkan pengguna menggulir konten virtual menggunakan keyboard mereka, pastikan bahwa elemen virtual atau kontainer gulir itu sendiri dapat difokuskan. Jika Anda gagal mengambil langkah ini, pengguliran keyboard tidak berfungsi di browser berbasis Chromium.
Misalnya, Anda dapat menggunakan tabindex
atribut pada kontainer gulir:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights">
<div class="flight-info">...</div>
</Virtualize>
</div>
Untuk mempelajari selengkapnya tentang arti nilai , , 0
atau nilai lainnya, lihattabindex
(dokumentasi MDN).-1
tabindex
Gaya tingkat lanjut dan deteksi gulir
Komponen Virtualize<TItem> ini hanya dirancang untuk mendukung mekanisme tata letak elemen tertentu. Untuk memahami tata letak elemen mana yang bekerja dengan benar, berikut ini menjelaskan cara Virtualize
mendeteksi elemen mana yang harus terlihat untuk ditampilkan di tempat yang benar.
Jika kode sumber Anda terlihat seperti berikut:
<div style="height:500px; overflow-y:scroll" tabindex="-1">
<Virtualize Items="allFlights" ItemSize="100">
<div class="flight-info">Flight @context.Id</div>
</Virtualize>
</div>
Pada runtime, Virtualize<TItem> komponen merender struktur DOM yang mirip dengan yang berikut ini:
<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>
Jumlah aktual baris yang dirender dan ukuran spacer bervariasi sesuai dengan gaya dan Items
ukuran koleksi Anda. Namun, perhatikan bahwa ada elemen spacer div
yang disuntikkan sebelum dan sesudah konten Anda. Ini melayani dua tujuan:
- Untuk memberikan offset sebelum dan sesudah konten Anda, menyebabkan item yang saat ini terlihat muncul di lokasi yang benar dalam rentang gulir dan rentang gulir itu sendiri untuk mewakili ukuran total semua konten.
- Untuk mendeteksi kapan pengguna menggulir di luar rentang yang terlihat saat ini, yang berarti bahwa konten yang berbeda harus dirender.
Catatan
Untuk mempelajari cara mengontrol tag elemen HTML spacer, lihat bagian Mengontrol nama tag elemen spacer nanti di artikel ini.
Elemen spacer secara internal menggunakan Intersection Observer untuk menerima pemberitahuan saat terlihat. Virtualize
tergantung pada penerimaan peristiwa ini.
Virtualize
bekerja dalam kondisi berikut:
Semua item konten yang dirender, termasuk konten tempat penampung, memiliki tinggi yang identik. Ini memungkinkan untuk menghitung konten mana yang sesuai dengan posisi gulir tertentu tanpa terlebih dahulu mengambil setiap item data dan merender data ke dalam elemen DOM.
Spasi dan baris konten dirender dalam satu tumpukan vertikal dengan setiap item mengisi seluruh lebar horizontal. Dalam kasus penggunaan umum,
Virtualize
bekerja dengandiv
elemen. Jika Anda menggunakan CSS untuk membuat tata letak yang lebih canggih, ingatlah persyaratan berikut:- Gaya kontainer gulir memerlukan
display
dengan salah satu nilai berikut:block
(default untukdiv
).table-row-group
(default untuktbody
).flex
denganflex-direction
diatur kecolumn
. Pastikan bahwa turunan Virtualize<TItem> langsung komponen tidak menyusut di bawah aturan flex. Misalnya, tambahkan.mycontainer > div { flex-shrink: 0 }
.
- Gaya baris konten memerlukan
display
dengan salah satu nilai berikut:block
(default untukdiv
).table-row
(default untuktr
).
- Jangan gunakan CSS untuk mengganggu tata letak untuk elemen spacer. Elemen spacer memiliki
display
nilaiblock
, kecuali jika induknya adalah grup baris tabel, dalam hal ini mereka default ketable-row
. Jangan mencoba memengaruhi lebar atau tinggi elemen spacer, termasuk dengan menyebabkannya memiliki batas ataucontent
elemen pseudo.
- Gaya kontainer gulir memerlukan
Pendekatan apa pun yang menghentikan spacer dan elemen konten dirender sebagai tumpukan vertikal tunggal, atau menyebabkan item konten bervariasi tingginya, mencegah fungsi komponen yang Virtualize<TItem> benar.
Virtualisasi tingkat akar
Komponen Virtualize<TItem> ini mendukung penggunaan dokumen itu sendiri sebagai akar gulir, sebagai alternatif untuk memiliki beberapa elemen lain dengan overflow-y: scroll
. Dalam contoh berikut, <html>
elemen atau <body>
ditata dalam komponen dengan overflow-y: scroll
:
<HeadContent>
<style>
html, body { overflow-y: scroll }
</style>
</HeadContent>
Komponen Virtualize<TItem> ini mendukung penggunaan dokumen itu sendiri sebagai akar gulir, sebagai alternatif untuk memiliki beberapa elemen lain dengan overflow-y: scroll
. Saat menggunakan dokumen sebagai akar gulir, hindari <html>
menata elemen atau <body>
dengan overflow-y: scroll
karena menyebabkan pengamat persimpangan memperlakukan tinggi halaman yang dapat digulir penuh sebagai wilayah yang terlihat, bukan hanya viewport jendela.
Anda dapat mereproduksi masalah ini dengan membuat daftar virtual besar (misalnya, 100.000 item) dan mencoba menggunakan dokumen sebagai akar gulir dengan html { overflow-y: scroll }
di halaman gaya CSS. Meskipun mungkin bekerja dengan benar kadang-kadang, browser mencoba merender semua 100.000 item setidaknya sekali pada awal penyajian, yang dapat menyebabkan penguncian tab browser.
Untuk mengatasi masalah ini sebelum rilis .NET 7, hindari elemen gaya <html>
/<body>
dengan overflow-y: scroll
atau adopsi pendekatan alternatif. Dalam contoh berikut, tinggi <html>
elemen diatur ke hanya lebih dari 100% dari tinggi viewport:
<HeadContent>
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
</HeadContent>
Komponen Virtualize<TItem> ini mendukung penggunaan dokumen itu sendiri sebagai akar gulir, sebagai alternatif untuk memiliki beberapa elemen lain dengan overflow-y: scroll
. Saat menggunakan dokumen sebagai akar gulir, hindari menata <html>
elemen atau <body>
dengan overflow-y: scroll
karena menyebabkan tinggi halaman yang dapat digulir penuh diperlakukan sebagai wilayah yang terlihat, bukan hanya viewport jendela.
Anda dapat mereproduksi masalah ini dengan membuat daftar virtual besar (misalnya, 100.000 item) dan mencoba menggunakan dokumen sebagai akar gulir dengan html { overflow-y: scroll }
di halaman gaya CSS. Meskipun mungkin bekerja dengan benar kadang-kadang, browser mencoba merender semua 100.000 item setidaknya sekali pada awal penyajian, yang dapat menyebabkan penguncian tab browser.
Untuk mengatasi masalah ini sebelum rilis .NET 7, hindari elemen gaya <html>
/<body>
dengan overflow-y: scroll
atau adopsi pendekatan alternatif. Dalam contoh berikut, tinggi <html>
elemen diatur ke hanya lebih dari 100% dari tinggi viewport:
<style>
html { min-height: calc(100vh + 0.3px) }
</style>
Mengontrol nama tag elemen spacer
Virtualize<TItem> Jika komponen ditempatkan di dalam elemen yang memerlukan nama tag anak tertentu, SpacerElement memungkinkan Anda untuk mendapatkan atau mengatur nama tag spacer virtualisasi. Nilai defaultnya adalah div
. Untuk contoh berikut, Virtualize<TItem> komponen merender di dalam elemen isi tabel (tbody
), sehingga elemen turunan yang sesuai untuk baris tabel (tr
) diatur sebagai spacer.
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();
}
Dalam contoh sebelumnya, akar dokumen digunakan sebagai kontainer gulir, sehingga html
elemen dan body
ditata dengan overflow-y: scroll
. Untuk informasi selengkapnya, lihat sumber daya berikut:
ASP.NET Core