Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
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 9 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 siklus hidup komponen ASP.NET Core Razor dan cara menggunakan peristiwa siklus hidup.
Peristiwa siklus hidup
Komponen Razor memproses Razor peristiwa siklus hidup komponen dalam satu set metode siklus hidup sinkron dan asinkron. Metode siklus hidup dapat diubah untuk melakukan operasi tambahan dalam komponen selama inisialisasi dan perenderan komponen.
Artikel ini menyederhanakan pemrosesan peristiwa siklus hidup komponen untuk mengklarifikasi logika kerangka kerja yang kompleks dan tidak mencakup setiap perubahan yang dilakukan selama bertahun-tahun. Anda mungkin perlu mengakses ComponentBase
sumber referensi untuk mengintegrasikan pemrosesan peristiwa kustom dengan Blazorpemrosesan peristiwa siklus hidup. Komentar kode di sumber referensi menyertakan komentar tambahan tentang pemrosesan peristiwa siklus hidup yang tidak muncul di artikel ini atau dalam dokumentasi API.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis yang spesifik, gunakan daftar tarik turun Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Diagram yang disederhanakan berikut mengilustrasikan Razor pemrosesan peristiwa siklus hidup komponen. Metode C# yang terkait dengan peristiwa siklus hidup didefinisikan dengan contoh di bagian berikut dari artikel ini.
Peristiwa siklus hidup komponen:
- Jika komponen dirender untuk pertama kalinya berdasarkan permintaan:
- Buat instans komponen.
- Lakukan injeksi properti.
- Panggil
OnInitialized{Async}
. Jika Task yang tidak lengkap dikembalikan, Task ditunggu, dan kemudian komponen dirender ulang. Metode sinkron dipanggil sebelum metode asinkron.
- Panggil
OnParametersSet{Async}
. Jika Task yang tidak lengkap dikembalikan, Task ditunggu, dan kemudian komponen dirender ulang. Metode sinkron dipanggil sebelum metode asinkron. - Render untuk semua pekerjaan sinkron dan selesaikan Task.
Catatan
Tindakan asinkron yang dilakukan dalam peristiwa siklus hidup mungkin menunda penyajian komponen atau menampilkan data. Untuk informasi selengkapnya, lihat bagian Menangani tindakan asinkron yang tidak lengkap di render nanti di artikel ini.
Komponen induk dirender sebelum komponen turunannya karena rendering adalah yang menentukan komponen turunan mana yang akan muncul. Jika inisialisasi komponen induk sinkron digunakan, inisialisasi induk dijamin selesai terlebih dahulu. Jika inisialisasi komponen induk asinkron digunakan, urutan penyelesaian inisialisasi komponen induk dan anak tidak dapat ditentukan karena bergantung pada kode inisialisasi yang berjalan.
Pemrosesan peristiwa DOM:
- Pengendali acara dijalankan.
- Jika Task yang tidak lengkap dikembalikan, Task ditunggu, dan kemudian komponen dirender ulang.
- Render untuk semua pekerjaan sinkron dan selesaikan Task.
Render
Siklus hidup:
- Hindari operasi penyajian lebih lanjut pada komponen ketika kedua kondisi berikut terpenuhi:
- Ini bukan render pertama.
-
ShouldRender
menghasilkanfalse
.
- Bangun perbedaan struktur tampilan dan tampilkan komponen.
- Tunggu sampai DOM diperbarui.
- Panggil
OnAfterRender{Async}
. Metode sinkron dipanggil sebelum metode asinkron.
Panggilan pengembang ke StateHasChanged
menyebabkan render ulang. Untuk informasi lebih lanjut, lihat perenderan komponen Razor ASP.NET Core.
Keadaan Diam selama pra-render
Di aplikasi Blazor sisi server, prarendering menunggu hingga kondisi sepi, yang berarti bahwa komponen tidak dirender sampai semua komponen di pohon render selesai dirender. Ketidakaktifan dapat menyebabkan penundaan yang nyata dalam penggambaran ketika suatu komponen melakukan tugas yang berlangsung dalam waktu lama selama inisialisasi dan metode siklus hidup lainnya, mengakibatkan pengalaman pengguna yang buruk. Untuk informasi selengkapnya, lihat bagian Menangani tindakan asinkron yang tidak lengkap di render nanti di artikel ini.
Ketika parameter diatur (SetParametersAsync
)
SetParametersAsync mengatur parameter yang disediakan oleh induk komponen di pohon render atau dari parameter rute.
Parameter metode ParameterView berisi sekumpulan nilai parameter komponen untuk komponen setiap kali SetParametersAsync dipanggil. Dengan mengesampingkan metode SetParametersAsync, kode pengembang dapat berinteraksi langsung dengan parameter ParameterView.
Implementasi
Umumnya, kode Anda harus memanggil metode kelas dasar (await base.SetParametersAsync(parameters);
) saat mengambil alih SetParametersAsync. Dalam skenario lanjutan, kode pengembang dapat menginterpretasikan nilai parameter masuk dengan cara apa pun yang diperlukan dengan tidak memanggil metode kelas dasar. Misalnya, tidak ada keharusan untuk menetapkan parameter yang diterima ke properti kelas. Namun, Anda harus merujuk ke ComponentBase
sumber referensi saat menyusun kode Anda tanpa memanggil metode kelas dasar karena memanggil metode siklus hidup lain dan memicu penyajian dengan cara yang kompleks.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis yang spesifik, gunakan daftar tarik turun Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Jika Anda ingin mengandalkan logika inisialisasi dan penyajian ComponentBase.SetParametersAsync tetapi tidak memproses parameter masuk, Anda memiliki opsi untuk meneruskan yang kosong ParameterView ke metode kelas dasar:
await base.SetParametersAsync(ParameterView.Empty);
Jika penangan acara disediakan dalam kode pengembang, lepaskan ketika dibuang. Untuk informasi selengkapnya, lihat RazorASP.NET Core .
Dalam contoh berikut, ParameterView.TryGetValue mengalokasikan nilai parameter Param
ke value
jika penguraian parameter rute untuk Param
berhasil. Jika value
tidak null
, nilai ditampilkan oleh komponen.
Meskipun pencocokan parameter rute tidak peka huruf besar/kecil, TryGetValue hanya mencocokkan nama parameter yang peka huruf besar/kecil dalam templat rute. Contoh berikut memerlukan penggunaan /{Param?}
dalam templat rute untuk mendapatkan nilai dengan TryGetValue, bukan /{param?}
. Jika /{param?}
digunakan dalam skenario ini, TryGetValue mengembalikan false
dan message
tidak diatur ke salah satu message
string.
SetParamsAsync.razor
:
@page "/set-params-async/{Param?}"
<PageTitle>Set Parameters Async</PageTitle>
<h1>Set Parameters Async Example</h1>
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<PageTitle>Set Parameters Async</PageTitle>
<h1>Set Parameters Async Example</h1>
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async"
@page "/set-params-async/{Param}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
Inisialisasi komponen (OnInitialized{Async}
)
OnInitialized dan OnInitializedAsync digunakan secara eksklusif untuk menginisialisasi komponen selama seluruh masa pakai instans komponen. Nilai parameter dan perubahan nilai parameter seharusnya tidak memengaruhi inisialisasi yang dilakukan dalam metode ini. Misalnya, memuat opsi statis ke dalam daftar dropdown yang tidak berubah selama masa pakai komponen dan itu tidak bergantung pada nilai parameter dilakukan dalam salah satu metode siklus hidup ini. Jika nilai parameter atau perubahan nilai parameter memengaruhi status komponen, gunakan OnParametersSet{Async}
sebagai gantinya.
Metode ini dipanggil ketika komponen diinisialisasi setelah menerima parameter awalnya di SetParametersAsync. Metode sinkron dipanggil sebelum metode asinkron.
Jika inisialisasi komponen induk sinkron digunakan, inisialisasi induk dijamin selesai sebelum inisialisasi komponen turunan. Jika inisialisasi komponen induk asinkron digunakan, urutan penyelesaian inisialisasi komponen induk dan anak tidak dapat ditentukan karena bergantung pada kode inisialisasi yang berjalan.
Untuk operasi sinkron, gantikan OnInitialized:
OnInit.razor
:
@page "/on-init"
<PageTitle>On Initialized</PageTitle>
<h1>On Initialized Example</h1>
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized() =>
message = $"Initialized at {DateTime.Now}";
}
@page "/on-init"
<PageTitle>On Initialized</PageTitle>
<h1>On Initialized Example</h1>
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized() =>
message = $"Initialized at {DateTime.Now}";
}
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
Untuk melakukan operasi asinkron, ambil alih OnInitializedAsync dan gunakan await
operator:
protected override async Task OnInitializedAsync()
{
await ...
}
Jika kelas dasar kustom dasar:
protected override async Task OnInitializedAsync()
{
await ...
await base.OnInitializedAsync();
}
Tidak perlu memanggil ComponentBase.OnInitializedAsync kecuali kelas dasar kustom digunakan dengan logika kustom. Untuk informasi selengkapnya, lihat bagian Metode siklus hidup kelas dasar.
Komponen harus memastikan berada dalam kondisi yang valid untuk rendering saat OnInitializedAsync menunggu Task yang mungkin tidak lengkap. Jika metode mengembalikan Taskyang tidak lengkap , bagian dari metode yang selesai secara sinkron harus meninggalkan komponen dalam keadaan valid untuk penyajian. Untuk informasi selengkapnya, lihat pernyataan pengantar ASP.NET Core Blazor konteks sinkronisasi dan ASP.NET Core Razor pembuangan komponen.
Blazor aplikasi yang merender terlebih dahulu konten mereka di server memanggil OnInitializedAsyncdua kali:
- Setelah komponen awalnya dirender secara statis sebagai bagian dari halaman.
- Ketika browser merender komponen untuk kedua kalinya.
Untuk mencegah kode pengembang di OnInitializedAsync agar tidak berjalan dua kali ketika pra-render, lihat bagian Koneksi ulang stateful setelah pra-render. Konten di bagian ini berfokus pada Blazor Web App dan koneksi ulang SignalRstateful. Untuk mempertahankan status selama eksekusi kode inisialisasi saat melakukan prarender, lihat Razor ASP.NET Core.
Untuk mencegah kode pengembang di OnInitializedAsync agar tidak berjalan dua kali ketika pra-render, lihat bagian Koneksi ulang stateful setelah pra-render. Meskipun konten di bagian ini berfokus pada
Saat Blazor aplikasi sedang pra-render, tindakan tertentu, seperti memanggil JavaScript (JS interop), tidak dimungkinkan. Komponen mungkin perlu dirender secara berbeda saat prarender. Untuk informasi selengkapnya, lihat bagian Prerendering dengan JavaScript interop.
Jika penangan acara disediakan dalam kode pengembang, lepaskan ketika dibuang. Untuk informasi selengkapnya, lihat RazorASP.NET Core .
Gunakan rendering streaming dengan rendering sisi server statis (SSR statis) atau prarendering untuk meningkatkan pengalaman pengguna untuk komponen yang melakukan tugas asinkron yang berjalan lama pada OnInitializedAsync untuk sepenuhnya merender. Untuk informasi selengkapnya, lihat sumber daya berikut:
Setelah parameter diatur (OnParametersSet{Async}
)
OnParametersSet atau OnParametersSetAsync dipanggil:
Setelah komponen diinisialisasi di OnInitialized atau OnInitializedAsync.
Ketika komponen induk merender ulang dan memberikan:
- Jenis yang diketahui atau primitif tidak dapat diubah ketika setidaknya satu parameter telah berubah.
- Parameter bertipe kompleks. Kerangka kerja tidak dapat mengetahui apakah nilai parameter berjenis kompleks telah bermutasi secara internal, sehingga kerangka kerja selalu memperlakukan parameter yang ditetapkan sebagai berubah ketika satu atau beberapa parameter berjenis kompleks ada.
Untuk informasi selengkapnya tentang konvensi penyajian, lihat Razor komponen ASP.NET Core.
Metode sinkron dipanggil sebelum metode asinkron.
Metode dapat dipanggil bahkan jika nilai parameter belum berubah. Perilaku ini menggaris bawahi kebutuhan pengembang untuk menerapkan logika tambahan dalam metode untuk memeriksa apakah nilai parameter memang telah berubah sebelum menginisialisasi ulang data atau status tergantung pada parameter tersebut.
Untuk komponen contoh berikut, navigasikan ke halaman komponen di URL:
- Dengan tanggal mulai yang telah diterima oleh
StartDate
:/on-parameters-set/2021-03-19
- Tanpa tanggal mulai, di mana
StartDate
ditetapkan nilai waktu lokal saat ini:/on-parameters-set
OnParamsSet.razor
:
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<PageTitle>On Parameters Set</PageTitle>
<h1>On Parameters Set Example</h1>
<p>
Pass a datetime in the URI of the browser's address bar.
For example, add <code>/1-1-2024</code> to the address.
</p>
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied " +
$"(StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used " +
$"(StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<PageTitle>On Parameters Set</PageTitle>
<h1>On Parameters Set Example</h1>
<p>
Pass a datetime in the URI of the browser's address bar.
For example, add <code>/1-1-2024</code> to the address.
</p>
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied " +
$"(StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used " +
$"(StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
Pekerjaan asinkron saat menerapkan parameter dan nilai properti harus terjadi selama peristiwa siklus hidup OnParametersSetAsync.
protected override async Task OnParametersSetAsync()
{
await ...
}
Jika kelas dasar kustom dasar:
protected override async Task OnParametersSetAsync()
{
await ...
await base.OnParametersSetAsync();
}
Tidak perlu memanggil ComponentBase.OnParametersSetAsync kecuali kelas dasar kustom digunakan dengan logika kustom. Untuk informasi selengkapnya, lihat bagian Metode siklus hidup kelas dasar.
Komponen harus memastikan berada dalam kondisi yang valid untuk rendering saat OnParametersSetAsync menunggu Task yang mungkin tidak lengkap. Jika metode mengembalikan Taskyang tidak lengkap , bagian dari metode yang selesai secara sinkron harus meninggalkan komponen dalam keadaan valid untuk penyajian. Untuk informasi selengkapnya, lihat pernyataan pengantar ASP.NET Core Blazor konteks sinkronisasi dan ASP.NET Core Razor pembuangan komponen.
Jika penangan acara disediakan dalam kode pengembang, lepaskan ketika dibuang. Untuk informasi selengkapnya, lihat RazorASP.NET Core .
Jika komponen sekali pakai tidak menggunakan CancellationToken, maka OnParametersSet dan OnParametersSetAsync harus memeriksa apakah komponen tersebut sudah dibuang. Jika OnParametersSetAsync mengembalikan Taskyang tidak lengkap , komponen harus memastikan bahwa bagian dari metode yang selesai secara sinkron meninggalkan komponen dalam status valid untuk penyajian. Untuk informasi selengkapnya, lihat pernyataan pengantar ASP.NET Core Blazor konteks sinkronisasi dan ASP.NET Core Razor pembuangan komponen.
Untuk informasi selengkapnya tentang parameter dan batasan rute, lihat Blazor inti ASP.NET.
Untuk contoh penerapan SetParametersAsync
secara manual untuk meningkatkan performa dalam beberapa skenario, lihat praktik terbaik performa penyajian ASP.NET CoreBlazor.
Setelah render komponen (OnAfterRender{Async}
)
OnAfterRender dan OnAfterRenderAsync dipanggil setelah komponen dirender secara interaktif dan UI telah selesai memperbarui (misalnya, setelah elemen ditambahkan ke DOM browser). Referensi elemen dan komponen sudah terisi pada saat ini. Gunakan tahap ini untuk melakukan langkah-langkah inisialisasi tambahan dengan konten yang dirender, seperti JS panggilan interop yang berinteraksi dengan elemen DOM yang dirender. Metode sinkron dipanggil sebelum metode asinkron.
Metode ini tidak dipanggil selama penyajian sebelumnya atau penyajian sisi server statis (SSR statis) di server karena proses tersebut tidak dilampirkan ke DOM browser langsung dan sudah selesai sebelum DOM diperbarui.
Untuk OnAfterRenderAsync, komponen tidak secara otomatis dirender ulang setelah penyelesaian setiap Task
yang dikembalikan untuk menghindari perulangan render tak terbatas.
OnAfterRender dan OnAfterRenderAsync dipanggil setelah komponen selesai dirender. Referensi elemen dan komponen sudah terisi pada saat ini. Gunakan tahap ini untuk melakukan langkah-langkah inisialisasi tambahan dengan konten yang dirender, seperti JS panggilan interop yang berinteraksi dengan elemen DOM yang dirender. Metode sinkron dipanggil sebelum metode asinkron.
Metode ini tidak dipanggil selama pra-penyajian karena pra-penyajian tidak dilampirkan ke DOM browser langsung dan sudah selesai sebelum DOM diperbarui.
Untuk OnAfterRenderAsync, komponen tidak secara otomatis dirender ulang setelah penyelesaian setiap Task
yang dikembalikan untuk menghindari perulangan render tak terbatas.
Parameter firstRender
untuk OnAfterRender dan OnAfterRenderAsync:
- Diatur pada
true
saat pertama kali instance komponen dirender. - Dapat digunakan untuk memastikan bahwa pekerjaan inisialisasi hanya dilakukan sekali.
AfterRender.razor
:
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender) =>
Logger.LogInformation("firstRender = {FirstRender}", firstRender);
private void HandleClick() => Logger.LogInformation("HandleClick called");
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender) =>
Logger.LogInformation("firstRender = {FirstRender}", firstRender);
private void HandleClick() => Logger.LogInformation("HandleClick called");
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
Sampel AfterRender.razor
menghasilkan output berikut ke konsol saat halaman dimuat dan tombol dipilih:
OnAfterRender: firstRender = True
HandleClick called
OnAfterRender: firstRender = False
Pekerjaan asinkron segera setelah penyajian harus terjadi selama OnAfterRenderAsync peristiwa siklus hidup:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
}
Jika kelas dasar kustom dasar:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
await base.OnAfterRenderAsync(firstRender);
}
Tidak perlu memanggil ComponentBase.OnAfterRenderAsync kecuali kelas dasar kustom digunakan dengan logika kustom. Untuk informasi selengkapnya, lihat bagian Metode siklus hidup kelas dasar.
Bahkan jika Anda mengembalikan Task dari OnAfterRenderAsync, kerangka kerja tidak menjadwalkan siklus render lebih lanjut untuk komponen Anda setelah tugas tersebut selesai. Ini untuk menghindari perulangan render tak terbatas. Ini berbeda dari metode siklus hidup lainnya, yang menjadwalkan siklus render berikutnya setelah Task yang dikembalikan selesai.
OnAfterRender dan OnAfterRenderAsynctidak dipanggil selama proses pra-penyajian di server. Metode dipanggil ketika komponen dirender secara interaktif setelah pra-perenderan. Saat aplikasi melakukan rendering awal:
- Komponen dijalankan pada server untuk menghasilkan beberapa markup HTML statis dalam respons HTTP. Selama fase ini, OnAfterRender dan OnAfterRenderAsync tidak dipanggil.
-
Blazor Ketika skrip (
blazor.{server|webassembly|web}.js
) dimulai di browser, komponen dimulai ulang dalam mode penyajian interaktif. Setelah komponen dimulai ulang, OnAfterRender dan OnAfterRenderAsyncdipanggil karena aplikasi tidak berada dalam fase pra-penyajian lagi.
Jika penangan acara disediakan dalam kode pengembang, lepaskan ketika dibuang. Untuk informasi selengkapnya, lihat RazorASP.NET Core .
Metode siklus hidup kelas dasar
Saat mengesampingkan metode siklus hidup Blazor, tidak perlu memanggil metode siklus hidup dari kelas dasar untuk ComponentBase. Namun, komponen harus memanggil metode siklus hidup dari kelas dasar yang diubahsuai dalam situasi berikut:
- Saat mengganti ComponentBase.SetParametersAsync,
await base.SetParametersAsync(parameters);
biasanya dipanggil karena metode kelas dasar seringkali memanggil metode siklus hidup lain dan memicu proses rendering dengan cara yang kompleks. Untuk informasi selengkapnya, lihat bagian Kapan parameter diatur (SetParametersAsync
). - Jika metode kelas dasar berisi logika yang harus dijalankan. Pengguna pustaka biasanya memanggil metode siklus hidup dari kelas dasar saat mewarisi kelas dasar karena kelas dasar pustaka sering memiliki logika siklus hidup khusus di dalamnya untuk dijalankan. Jika aplikasi menggunakan kelas dasar dari pustaka, lihat dokumentasi pustaka untuk panduan.
Dalam contoh berikut, base.OnInitialized();
dipanggil untuk memastikan bahwa metode kelas OnInitialized
dasar dijalankan. Tanpa panggilan, BlazorRocksBase2.OnInitialized
tidak akan dieksekusi.
BlazorRocks2.razor
:
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@using Microsoft.Extensions.Logging
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@using Microsoft.Extensions.Logging
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
BlazorRocksBase2.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
protected override void OnInitialized() =>
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
protected override void OnInitialized() =>
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
Perubahan status (StateHasChanged
)
StateHasChanged memberi tahu komponen bahwa statusnya telah berubah. Jika berlaku, memanggil StateHasChanged akan mengantrekan penggambaran ulang yang terjadi saat utas utama aplikasi bebas.
StateHasChanged dipanggil secara otomatis untuk EventCallback metode. Untuk informasi selengkapnya tentang panggilan balik peristiwa, lihat ASP.NET Core Blazor penanganan peristiwa.
Untuk informasi selengkapnya tentang penyajian komponen dan kapan harus memanggil StateHasChanged, termasuk kapan harus memanggilnya dengan ComponentBase.InvokeAsync, lihat ASP.NET Core Razor penyajian komponen.
Menangani tindakan asinkron yang belum selesai saat proses rendering
Tindakan asinkron yang dilakukan dalam peristiwa siklus hidup mungkin belum selesai sebelum komponen dirender. Objek mungkin null
atau datanya belum sepenuhnya terisi saat metode siklus hidup sedang dijalankan. Berikan logika penyajian untuk mengonfirmasi bahwa objek diinisialisasi. Tampilkan elemen UI placeholder (misalnya, pesan pemuatan) ketika objek null
.
Dalam komponen Slow
berikut, OnInitializedAsync dioverride untuk mengeksekusi tugas yang berjalan lama secara asinkron. Saat isLoading
adalah true
, pesan memuat ditampilkan kepada pengguna. Setelah Task
yang dikembalikan oleh OnInitializedAsync selesai diproses, komponen dirender ulang dengan kondisi yang telah diperbarui, memperlihatkan pesan "Finished!
".
Slow.razor
:
@page "/slow"
<h2>Slow Component</h2>
@if (isLoading)
{
<div><em>Loading...</em></div>
}
else
{
<div>Finished!</div>
}
@code {
private bool isLoading = true;
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
isLoading = false;
}
private Task LoadDataAsync()
{
return Task.Delay(10000);
}
}
Komponen sebelumnya menggunakan variabel isLoading
untuk menampilkan pesan pemuatan. Pendekatan serupa digunakan untuk komponen yang memuat data ke dalam kumpulan dan memeriksa apakah kumpulan tersebut adalah null
untuk menampilkan pesan pemuatan. Contoh berikut memeriksa koleksi movies
untuk apakah null
akan menampilkan pesan pemuatan atau menampilkan kumpulan film.
@if (movies == null)
{
<p><em>Loading...</em></p>
}
else
{
@* display movies *@
}
@code {
private Movies[]? movies;
protected override async Task OnInitializedAsync()
{
movies = await GetMovies();
}
}
Prerendering menunggu quiescence , yang berarti bahwa sebuah komponen tidak akan melakukan render sampai semua komponen di dalam pohon render telah selesai dirender. Ini berarti bahwa pesan pemuatan tidak ditampilkan saat metode OnInitializedAsync dari komponen anak menjalankan tugas yang memakan waktu lama selama pra-rendering. Untuk menunjukkan perilaku ini, tempatkan komponen Slow
sebelumnya ke dalam komponen Home
aplikasi pengujian:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<Slow />
Catatan
Meskipun contoh di bagian ini membahas metode siklus hidup OnInitializedAsync, metode siklus hidup lain yang dijalankan selama pra-penyajian dapat menunda penyajian akhir komponen. Hanya OnAfterRender{Async}
yang tidak dieksekusi selama prarender dan kebal terhadap penundaan karena keadaan diam.
Selama pra-penyajian, komponen Home
tidak merender hingga komponen Slow
dirender, yang membutuhkan waktu sepuluh detik. UI kosong selama periode sepuluh detik ini, dan tidak ada pesan pemuatan. Setelah pra-rendering, komponen Home
memulai rendering, dan pesan loading komponen Slow
ditampilkan. Setelah sepuluh detik lagi, komponen Slow
akhirnya menampilkan pesan yang sudah selesai.
Seperti yang diilustrasikan oleh demonstrasi sebelumnya, ketidakaktifan selama prarender menghasilkan pengalaman pengguna yang buruk. Untuk meningkatkan pengalaman pengguna, mulailah dengan menerapkan penyajian streaming untuk menghindari menunggu tugas asinkron selesai saat melakukan pra-penyajian.
Tambahkan atribut [StreamRendering]
ke komponen Slow
(gunakan [StreamRendering(true)]
di .NET 8):
@attribute [StreamRendering]
Ketika komponen Home
dalam proses prarender, komponen Slow
dengan cepat dirender dengan pesannya sedang dimuat. Komponen Home
tidak menunggu selama sepuluh detik hingga komponen Slow
selesai dirender. Namun, pesan penyelesaian yang ditampilkan di akhir prarender digantikan oleh pesan pemuatan hingga akhirnya komponen dirender, yang menunda sepuluh detik lagi. Ini terjadi karena komponen Slow
dirender dua kali, bersama dengan LoadDataAsync
mengeksekusi dua kali. Saat komponen mengakses sumber daya, seperti layanan dan database, eksekusi ganda panggilan layanan dan kueri database membuat beban yang tidak diinginkan pada sumber daya aplikasi.
Untuk mengatasi penyajian ganda pesan pemuatan dan eksekusi ulang panggilan layanan dan database, pertahankan status yang telah dirender sebelumnya dengan PersistentComponentState untuk penyajian akhir komponen, seperti yang terlihat dalam pembaruan berikut untuk komponen Slow
:
@page "/slow"
@attribute [StreamRendering]
<h2>Slow Component</h2>
@if (Data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@Data</div>
}
@code {
[SupplyParameterFromPersistentComponentState]
public string? Data { get; set; }
protected override async Task OnInitializedAsync()
{
Data ??= await LoadDataAsync();
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
}
@page "/slow"
@attribute [StreamRendering]
@implements IDisposable
@inject PersistentComponentState ApplicationState
<h2>Slow Component</h2>
@if (data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@data</div>
}
@code {
private string? data;
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<string>(nameof(data), out var restored))
{
data = await LoadDataAsync();
}
else
{
data = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson(nameof(data), data);
return Task.CompletedTask;
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
Dengan menggabungkan penyajian streaming dengan status komponen persisten:
- Layanan dan database hanya memerlukan satu panggilan untuk inisialisasi komponen.
- Komponen merender UI mereka dengan cepat dengan memuat pesan selama tugas jangka panjang untuk pengalaman pengguna terbaik.
Untuk informasi selengkapnya, lihat sumber daya berikut:
:::moniker-end
Keheningan selama prarendering menghasilkan pengalaman pengguna yang buruk. Penundaan dapat diatasi di aplikasi yang menargetkan .NET 8 atau yang lebih baru dengan fitur bernama penyajian streaming, biasanya dikombinasikan dengan menyimpan status komponen selama pra-penyajian untuk menghindari penantian hingga tugas asinkron selesai. Dalam versi .NET yang lebih lama dari .NET 8, menjalankan tugas latar belakang yang berjalan lama yang memuat data setelah render akhir dapat mengatasi penundaan render yang lama karena keheningan.
Menangani kesalahan
Untuk informasi tentang penanganan kesalahan selama eksekusi metode siklus hidup, lihat Blazor ASP.NET Core.
Koneksi ulang berstatus setelah prapenyajian
Saat melakukan pra-penyajian di server, komponen awalnya dirender secara statis sebagai bagian dari halaman. Setelah browser membuat SignalR koneksi kembali ke server, komponen diolah lagi dan menjadi interaktif.
OnInitialized{Async}
Jika metode siklus hidup untuk menginisialisasi komponen ada, metode dijalankan dua kali:
- Ketika komponen diprarender secara statis.
- Setelah koneksi server dibuat.
Ini dapat mengakibatkan perubahan nyata dalam data yang ditampilkan di UI ketika komponen akhirnya dirender. Untuk menghindari perilaku ini, berikan pengidentifikasi untuk menyimpan status saat pra-rendering dan untuk mengambil status setelah pra-rendering.
Kode berikut menunjukkan WeatherForecastService
yang menghindari perubahan tampilan data karena prarendering. Yang ditunggu (Delay) mensimulasikan penundaan singkat (await Task.Delay(...)
) sebelum mengembalikan data dari metode (GetForecastAsync
).
Tambahkan layanan IMemoryCache dengan AddMemoryCache ke koleksi layanan di file aplikasi Program
.
builder.Services.AddMemoryCache();
WeatherForecastService.cs
:
using Microsoft.Extensions.Caching.Memory;
namespace BlazorSample;
public class WeatherForecastService(IMemoryCache memoryCache)
{
private static readonly string[] summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
public IMemoryCache MemoryCache { get; } = memoryCache;
public Task<WeatherForecast[]?> GetForecastAsync(DateOnly startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
namespace BlazorSample;
public class WeatherForecastService(IMemoryCache memoryCache)
{
private static readonly string[] summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
public IMemoryCache MemoryCache { get; } = memoryCache;
public Task<WeatherForecast[]?> GetForecastAsync(DateOnly startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]?> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
var rng = new Random();
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = summaries[rng.Next(summaries.Length)]
}).ToArray();
});
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
var rng = new Random();
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = summaries[rng.Next(summaries.Length)]
}).ToArray();
});
}
}
Untuk informasi selengkapnya tentang RenderMode, lihat Blazor ASP.NET CoreSignalR.
Konten di bagian ini berfokus pada Blazor Web Apps dan SignalRkoneksi ulang stateful. Untuk mempertahankan status selama eksekusi kode inisialisasi saat melakukan prarender, lihat Razor ASP.NET Core.
Meskipun konten di bagian ini berfokus pada Blazor Server dan SignalRkoneksi ulang yang stateful, skenario untuk prarender dalam solusi dihosting Blazor WebAssembly (WebAssemblyPrerendered) melibatkan kondisi dan pendekatan serupa untuk mencegah eksekusi kode pengembang dua kali. Untuk mempertahankan status selama eksekusi kode inisialisasi saat melakukan prarender, lihat Mengintegrasikan komponen ASP.NET Core Razor dengan MVC atau Razor Pages.
Prarendering dengan interoperabilitas JavaScript
Bagian ini berlaku untuk aplikasi sisi server yang melakukan prarender komponen Razor. Prerendering dibahas dalam komponen Prerender ASP.NET CoreRazor.
Catatan
Navigasi internal untuk perutean interaktif pada Blazor Web App tidak melibatkan permintaan konten halaman baru dari server. Oleh karena itu, prerendering tidak terjadi untuk permintaan halaman internal. Jika aplikasi mengadopsi perutean interaktif, lakukan memuat ulang halaman secara penuh untuk contoh komponen yang menunjukkan perilaku prarender. Untuk informasi selengkapnya, lihat Prarender komponen ASP.NET CoreRazor.
Bagian ini berlaku untuk aplikasi sisi server dan aplikasi yang dihosting yang melakukan prarender komponen Blazor WebAssembly. Prarendering tercakup dalam Mengintegrasikan komponen ASP.NET Core Razor dengan MVC atau Razor Halaman.
Selama prarendering, memanggil JavaScript (JS) tidak memungkinkan. Contoh berikut menunjukkan cara menggunakan JS interop sebagai bagian dari logika inisialisasi komponen dengan cara yang kompatibel dengan pra-penyajian.
Berikut adalah fungsi scrollElementIntoView
:
- Menggulir ke elemen yang ditentukan dengan
scrollIntoView
. - Mengembalikan nilai properti elemen
top
darigetBoundingClientRect
metode .
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Dalam kode komponen, IJSRuntime.InvokeAsync memanggil fungsi JS, di mana ElementReference hanya digunakan dalam OnAfterRenderAsync dan tidak pada metode siklus hidup sebelumnya karena elemen HTML DOM baru ada setelah komponen dirender.
StateHasChanged
(sumber referensi) dipanggil untuk mengantrekan perenderan komponen dengan status baru yang diperoleh dari JS panggilan interop (untuk informasi selengkapnya, lihat ASP.NET Core Razor penyajian komponen). Perulangan tak terbatas tidak dibuat karena StateHasChanged hanya dipanggil ketika scrollPosition
adalah null
.
PrerenderedInterop.razor
:
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<PageTitle>Prerendered Interop</PageTitle>
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
Contoh sebelumnya mengotori pengguna dengan fungsi global. Untuk pendekatan yang lebih baik dalam aplikasi produksi, lihat Isolasi JavaScript dalam modul JavaScript.
Pekerjaan latar belakang yang dapat dibatalkan
Komponen sering melakukan pekerjaan latar belakang yang berjalan lama, seperti melakukan panggilan jaringan (HttpClient) dan berinteraksi dengan database. Disarankan untuk menghentikan pekerjaan latar belakang untuk menghemat sumber daya sistem dalam beberapa situasi. Misalnya, operasi asinkron latar belakang tidak secara otomatis berhenti saat pengguna menavigasi jauh dari komponen.
Alasan lain mengapa item kerja latar belakang mungkin memerlukan pembatalan meliputi:
- Tugas latar belakang yang dijalankan dimulai dengan data input yang rusak atau parameter pemrosesan.
- Kumpulan item kerja latar belakang yang dijalankan saat ini harus diganti dengan sekumpulan item kerja baru.
- Prioritas tugas yang saat ini dijalankan harus diubah.
- Aplikasi harus ditutup untuk penggelaran ulang server.
- Sumber daya server menjadi terbatas, membutuhkan penjadwalan ulang item kerja latar belakang.
Untuk menerapkan pola kerja latar belakang yang dapat dibatalkan dalam komponen:
- Gunakan CancellationTokenSource dan CancellationToken.
- Setelah pembuangan komponen dan kapan saja pembatalan diinginkan dengan membatalkan token secara manual, panggil
CancellationTokenSource.Cancel
untuk memberi sinyal agar pekerjaan latar belakang dapat dibatalkan. - Setelah panggilan asinkron selesai, panggil ThrowIfCancellationRequested pada token.
Dalam contoh berikut:
-
await Task.Delay(10000, cts.Token);
mewakili proses latar belakang asinkron yang berjalan lama. -
BackgroundResourceMethod
mewakili metode latar belakang yang berjalan lama yang seharusnya tidak dimulai jikaResource
dibuang sebelum metode dipanggil.
BackgroundWork.razor
:
@page "/background-work"
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<PageTitle>Background Work</PageTitle>
<h1>Background Work Example</h1>
<p>
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
</p>
<p>Study logged messages in the console.</p>
<p>
If you trigger disposal within 10 seconds of page load, the
<code>BackgroundResourceMethod</code> isn't executed.
</p>
<p>
If disposal occurs after <code>BackgroundResourceMethod</code> is called but
before action is taken on the resource, an <code>ObjectDisposedException</code>
is thrown by <code>BackgroundResourceMethod</code>, and the resource isn't
processed.
</p>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
private IList<string> messages = [];
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(10000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts?.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose() => disposed = true;
}
}
@page "/background-work"
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<PageTitle>Background Work</PageTitle>
<h1>Background Work Example</h1>
<p>
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
</p>
<p>Study logged messages in the console.</p>
<p>
If you trigger disposal within 10 seconds of page load, the
<code>BackgroundResourceMethod</code> isn't executed.
</p>
<p>
If disposal occurs after <code>BackgroundResourceMethod</code> is called but
before action is taken on the resource, an <code>ObjectDisposedException</code>
is thrown by <code>BackgroundResourceMethod</code>, and the resource isn't
processed.
</p>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
private IList<string> messages = [];
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(10000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts?.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose() => disposed = true;
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new Resource();
private CancellationTokenSource cts = new CancellationTokenSource();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
Untuk menampilkan indikator pemuatan saat pekerjaan latar belakang sedang berlangsung, gunakan pendekatan berikut.
Buat komponen indikator pemuatan dengan parameter Loading
yang dapat menampilkan isi bawaan dalam RenderFragment. Untuk parameter: Loading
- Saat
true
, tampilkan indikator pemuatan. - Ketika
false
, tampilkan konten komponen (ChildContent
). Untuk informasi selengkapnya, lihat Fragmen render konten anak.
ContentLoading.razor
:
@if (Loading)
{
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
}
else
{
@ChildContent
}
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public bool Loading { get; set; }
}
Untuk memuatkan gaya CSS untuk indikator, tambahkan gaya tersebut ke dalam konten <head>
menggunakan komponen HeadContent. Untuk informasi selengkapnya, lihat Blazor.
@if (Loading)
{
<!-- OPTIONAL ...
<HeadContent>
<style>
...
</style>
</HeadContent>
-->
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
}
else
{
@ChildContent
}
...
Bungkus markup Razor dengan komponen ContentLoading
dan berikan nilai dari bidang C# ke parameter Loading
ketika komponen melakukan pekerjaan inisialisasi.
<ContentLoading Loading="@loading">
...
</ContentLoading>
@code {
private bool loading = true;
...
protected override async Task OnInitializedAsync()
{
await LongRunningWork().ContinueWith(_ => loading = false);
}
...
}
Blazor Server kejadian koneksi ulang
Peristiwa siklus hidup komponen yang tercakup dalam artikel ini beroperasi secara terpisah dari penangan acara koneksi ulang sisi server. SignalR Ketika koneksi ke klien hilang, hanya pembaruan UI yang terganggu. Pembaruan UI dilanjutkan saat koneksi dibuat ulang. Untuk informasi selengkapnya tentang peristiwa dan konfigurasi penanganan sirkuit, lihat Blazor ASP.NET CoreSignalR.
Sumber Daya Tambahan:
Menangani pengecualian yang tertangkap di luar siklus hidup suatu komponen
ASP.NET Core