siklus hidup komponen ASP.NET Core Razor
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 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 8 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 ditimpa untuk melakukan operasi tambahan dalam komponen selama inisialisasi dan penyajian 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 rilis tertentu, gunakan daftar dropdown 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 tidak lengkap Task dikembalikan, Task ditunggu dan kemudian komponen dirender. Metode sinkron dipanggil sebelum metode asinkron.
- Panggil
OnParametersSet{Async}
. Jika tidak lengkap Task dikembalikan, Task ditunggu dan kemudian komponen dirender. Metode sinkron dipanggil sebelum metode asinkron. - Render untuk semua pekerjaan sinkron dan selesai Task.
Catatan
Tindakan asinkron yang dilakukan dalam peristiwa siklus hidup mungkin tidak selesai sebelum komponen dirender. Untuk informasi selengkapnya, lihat bagian Menangani tindakan asinkron yang tidak lengkap di render nanti di artikel ini.
Komponen induk dirender sebelum komponen turunannya karena penyajian adalah apa yang menentukan turunan mana yang ada. 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:
- Penanganan aktivitas dijalankan.
- Jika tidak lengkap Task dikembalikan, Task ditunggu dan kemudian komponen dirender.
- Render untuk semua pekerjaan sinkron dan selesai Task.
Render
Siklus hidup:
- Hindari operasi penyajian lebih lanjut pada komponen ketika kedua kondisi berikut terpenuhi:
- Ini bukan render pertama.
ShouldRender
menghasilkanfalse
.
- Buat diff pohon render (perbedaan) dan render komponen.
- Tunggu DOM untuk diperbarui.
- Panggil
OnAfterRender{Async}
. Metode sinkron dipanggil sebelum metode asinkron.
Panggilan pengembang untuk StateHasChanged
menghasilkan rerender. Untuk informasi lebih lanjut, lihat perenderan komponen Razor ASP.NET Core.
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 SetParametersAsync metode , kode pengembang dapat berinteraksi langsung dengan ParameterViewparameter .
Implementasi SetParametersAsync default menetapkan nilai setiap properti dengan [Parameter]
atribut atau [CascadingParameter]
yang memiliki nilai yang sesuai dalam ParameterView. Parameter yang tidak memiliki nilai yang sesuai dibiarkan ParameterView tidak berubah.
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 persyaratan untuk menetapkan parameter masuk 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 rilis tertentu, gunakan daftar dropdown 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 penanganan aktivitas disediakan dalam kode pengembang, lepaskan di pembuangan. Untuk informasi selengkapnya, lihat bagian Dan pembuangan IDisposable
IAsyncDisposable
komponen.
Dalam contoh berikut, ParameterView.TryGetValue menetapkan Param
nilai parameter ke value
jika penguraian parameter rute berhasil Param
. Jika value
tidak null
, nilai ditampilkan oleh komponen.
Meskipun pencocokan parameter rute tidak peka huruf besar/kecil, TryGetValue hanya cocok dengan nama parameter 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, ambil alih 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 digunakan dengan logika inisialisasi kustom, panggil OnInitializedAsync di kelas 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.
Blazor aplikasi yang merender konten mereka di panggilan OnInitializedAsync server dua kali:
- Setelah komponen awalnya dirender secara statis sebagai bagian dari halaman.
- Untuk kedua kalinya ketika browser merender komponen.
Untuk mencegah kode pengembang masuk OnInitializedAsync agar tidak berjalan dua kali saat melakukan pra-penyajian, lihat bagian Koneksi ulang stateful setelah pra-penyajian . Konten di bagian berfokus pada Blazor Web Appkoneksi ulang s dan statefulSignalR. Untuk mempertahankan status selama eksekusi kode inisialisasi saat melakukan prarender, lihat Prarender komponen ASP.NET CoreRazor.
Untuk mencegah kode pengembang masuk OnInitializedAsync agar tidak berjalan dua kali saat melakukan pra-penyajian, lihat bagian Koneksi ulang stateful setelah pra-penyajian . Meskipun konten di bagian berfokus pada Blazor Server dan koneksi ulang yang statefulSignalR, skenario untuk prarender dalam solusi yang Blazor WebAssembly dihosting (WebAssemblyPrerendered) melibatkan kondisi dan pendekatan serupa untuk mencegah menjalankan kode pengembang dua kali. Untuk mempertahankan status selama eksekusi kode inisialisasi saat melakukan prarender, lihat Merender dan mengintegrasikan komponen ASP.NET CoreRazor.
Blazor Saat aplikasi melakukan pra-penyajian, tindakan tertentu, seperti memanggil ke JavaScript (JS interop), tidak dimungkinkan. Komponen mungkin perlu dirender secara berbeda saat dirender sebelumnya. Untuk informasi selengkapnya, lihat bagian Pra-penyajian dengan interop JavaScript.
Jika penanganan aktivitas disediakan dalam kode pengembang, lepaskan di pembuangan. Untuk informasi selengkapnya, lihat bagian Pembuangan komponen denganIAsyncDisposable
IDisposable
.
Gunakan penyajian streaming dengan penyajian sisi server statis (SSR statis) atau pra-penyajian untuk meningkatkan pengalaman pengguna untuk komponen yang melakukan tugas OnInitializedAsync asinkron yang berjalan lama untuk sepenuhnya merender. Untuk informasi lebih lanjut, lihat perenderan komponen Razor ASP.NET Core.
Setelah parameter diatur (OnParametersSet{Async}
)
OnParametersSet atau OnParametersSetAsync dipanggil:
Setelah komponen diinisialisasi di OnInitialized atau OnInitializedAsync.
Ketika komponen induk merender dan memasok:
- Jenis yang diketahui atau primitif tidak dapat diubah ketika setidaknya satu parameter telah berubah.
- Parameter berjenis 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 penyajian komponen ASP.NET CoreRazor.
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 diterima oleh
StartDate
:/on-parameters-set/2021-03-19
- Tanpa tanggal mulai, di mana
StartDate
ditetapkan nilai waktu lokal saat ini:/on-parameters-set
Catatan
Dalam rute komponen, tidak dimungkinkan untuk membatasi parameter dengan batasan datetime
rute dan membuat parameter opsional.DateTime Oleh karena itu, komponen berikut OnParamsSet
menggunakan dua @page
arahan untuk menangani perutean dengan dan tanpa segmen tanggal yang disediakan di URL.
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 OnParametersSetAsync peristiwa siklus hidup:
protected override async Task OnParametersSetAsync()
{
await ...
}
Jika kelas dasar kustom digunakan dengan logika inisialisasi kustom, panggil OnParametersSetAsync di kelas 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.
Jika penanganan aktivitas disediakan dalam kode pengembang, lepaskan di pembuangan. Untuk informasi selengkapnya, lihat bagian Pembuangan komponen denganIAsyncDisposable
IDisposable
.
Untuk informasi selengkapnya tentang parameter dan batasan rute, lihat perutean dan navigasi inti Blazor ASP.NET.
Untuk contoh penerapan SetParametersAsync
secara manual untuk meningkatkan performa dalam beberapa skenario, lihat praktik terbaik performa 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 diisi 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 yang dikembalikan Task
untuk menghindari perulangan render tak terbatas.
OnAfterRender dan OnAfterRenderAsync dipanggil setelah komponen selesai dirender. Referensi elemen dan komponen diisi 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 yang dikembalikan Task
untuk menghindari perulangan render tak terbatas.
Parameter firstRender
untuk OnAfterRender dan OnAfterRenderAsync:
- Diatur ke
true
pertama kali instans 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 digunakan dengan logika inisialisasi kustom, panggil OnAfterRenderAsync di kelas 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 lebih lanjut setelah dikembalikan Task selesai.
OnAfterRender dan OnAfterRenderAsync tidak dipanggil selama proses pra-penyajian di server. Metode dipanggil ketika komponen dirender secara interaktif setelah pra-penyajian. Saat aplikasi melakukan pra-penyajian:
- 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 OnAfterRenderAsync dipanggil karena aplikasi tidak berada dalam fase pra-penyajian lagi.
Jika penanganan aktivitas disediakan dalam kode pengembang, lepaskan di pembuangan. Untuk informasi selengkapnya, lihat bagian Pembuangan komponen denganIAsyncDisposable
IDisposable
.
Metode siklus hidup kelas dasar
Saat mengesampingkan Blazormetode siklus hidup, tidak perlu memanggil metode siklus hidup kelas dasar untuk ComponentBase. Namun, komponen harus memanggil metode siklus hidup kelas dasar yang ditimpa dalam situasi berikut:
- Saat mengambil ComponentBase.SetParametersAsyncalih ,
await base.SetParametersAsync(parameters);
biasanya dipanggil karena metode kelas dasar memanggil metode siklus hidup lain dan memicu penyajian dengan cara yang kompleks. Untuk informasi selengkapnya, lihat bagian Kapan parameter diatur (SetParametersAsync
). - Jika metode kelas dasar berisi logika yang harus dijalankan. Konsumen pustaka biasanya memanggil metode siklus hidup kelas dasar saat mewarisi kelas dasar karena kelas dasar pustaka sering memiliki logika siklus hidup kustom 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 dijalankan.
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, panggilan mengantrekan StateHasChanged rerender yang terjadi saat utas utama aplikasi gratis.
StateHasChanged dipanggil secara otomatis untuk EventCallback metode. Untuk informasi selengkapnya tentang panggilan balik peristiwa, lihat penanganan peristiwa ASP.NET CoreBlazor.
Untuk informasi selengkapnya tentang penyajian komponen dan kapan harus memanggil StateHasChanged, termasuk kapan harus memanggilnya dengan ComponentBase.InvokeAsync, lihat ASP.NET penyajian komponen CoreRazor.
Menangani tindakan asinkron yang tidak lengkap saat dirender
Tindakan asinkron yang dilakukan dalam peristiwa siklus hidup mungkin belum selesai sebelum komponen dirender. Objek mungkin null
atau tidak lengkap diisi dengan data saat metode siklus hidup sedang dijalankan. Berikan logika penyajian untuk mengonfirmasi bahwa objek diinisialisasi. Merender elemen UI tempat penampung (misalnya, pesan pemuatan) sementara objek adalah null
.
Dalam komponen berikut, OnInitializedAsync ditimpa untuk secara asinkron menyediakan data peringkat film (movies
). Ketika movies
adalah null
, pesan pemuatan ditampilkan kepada pengguna. Task
Setelah dikembalikan oleh OnInitializedAsync selesai, komponen dirender dengan status yang diperbarui.
<h1>Sci-Fi Movie Ratings</h1>
@if (movies == null)
{
<p><em>Loading...</em></p>
}
else
{
<ul>
@foreach (var movie in movies)
{
<li>@movie.Title — @movie.Rating</li>
}
</ul>
}
@code {
private Movies[]? movies;
protected override async Task OnInitializedAsync()
{
movies = await GetMovieRatings(DateTime.Now);
}
}
Menangani kesalahan
Untuk informasi tentang penanganan kesalahan selama eksekusi metode siklus hidup, lihat Menangani kesalahan di aplikasi ASP.NET CoreBlazor.
Koneksi ulang stateful setelah pra-penyajian
Saat melakukan pra-penyajian di server, komponen awalnya dirender secara statis sebagai bagian dari halaman. Setelah browser membuat SignalR koneksi kembali ke server, komponen dirender lagi dan interaktif. OnInitialized{Async}
Jika metode siklus hidup untuk menginisialisasi komponen ada, metode dijalankan dua kali:
- Ketika komponen dirender 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, teruskan pengidentifikasi untuk menyimpan status selama pra-penyajian dan untuk mengambil status setelah pra-penyajian.
Kode berikut menunjukkan WeatherForecastService
yang menghindari perubahan tampilan data karena pra-penyajian. Yang ditunggu (await Task.Delay(...)
) mensimulasikan Delay penundaan singkat sebelum mengembalikan data dari GetForecastAsync
metode .
Tambahkan IMemoryCache layanan dengan AddMemoryCache pada koleksi layanan dalam 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 panduan ASP.NET CoreBlazorSignalR.
Konten di bagian ini berfokus pada Blazor Web Appkoneksi ulang s dan statefulSignalR. Untuk mempertahankan status selama eksekusi kode inisialisasi saat melakukan prarender, lihat Prarender komponen ASP.NET CoreRazor.
Meskipun konten di bagian ini berfokus pada Blazor Server dan koneksi ulang yang statefulSignalR, skenario untuk prarender dalam solusi yang Blazor WebAssembly dihosting (WebAssemblyPrerendered) melibatkan kondisi dan pendekatan serupa untuk mencegah eksekusi kode pengembang dua kali. Untuk mempertahankan status selama eksekusi kode inisialisasi saat melakukan prarender, lihat Merender dan mengintegrasikan komponen ASP.NET CoreRazor.
Pra-penyajian dengan interop JavaScript
Bagian ini berlaku untuk aplikasi sisi server yang merender Razor komponen sebelumnya. Pra-penyajian tercakup dalam komponen Prerender ASP.NET CoreRazor.
Catatan
Navigasi internal untuk perutean interaktif di Blazor Web Apptidak melibatkan permintaan konten halaman baru dari server. Oleh karena itu, pra-penyajian tidak terjadi untuk permintaan halaman internal. Jika aplikasi mengadopsi perutean interaktif, lakukan pemuatan ulang halaman 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 Blazor WebAssembly yang melakukan prarender Razor komponen. Pra-penyajian tercakup dalam Prarender dan mengintegrasikan komponen ASP.NET CoreRazor.
Selama pra-penyajian, panggilan ke JavaScript (JS) tidak dimungkinkan. Contoh berikut menunjukkan cara menggunakan JS interop sebagai bagian dari logika inisialisasi komponen dengan cara yang kompatibel dengan pra-penyajian.
Fungsi berikut scrollElementIntoView
:
- Menggulir ke elemen yang diteruskan dengan
scrollIntoView
. - Mengembalikan nilai properti elemen
top
darigetBoundingClientRect
metode .
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Di mana IJSRuntime.InvokeAsync memanggil JS fungsi dalam kode komponen, satu-satunya ElementReference digunakan dalam OnAfterRenderAsync dan bukan dalam metode siklus hidup sebelumnya karena tidak ada elemen HTML DOM sampai setelah komponen dirender.
StateHasChanged
(sumber referensi) dipanggil untuk mengantrekan penyajian komponen dengan status baru yang diperoleh dari JS panggilan interop (untuk informasi selengkapnya, lihat ASP.NET penyajian komponen CoreRazor). 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 mencemari klien dengan fungsi global. Untuk pendekatan yang lebih baik dalam aplikasi produksi, lihat Isolasi JavaScript dalam modul JavaScript.
Pembuangan komponen dengan IDisposable
dan IAsyncDisposable
Jika komponen mengimplementasikan IDisposable atau IAsyncDisposable, kerangka kerja memanggil pembuangan sumber daya saat komponen dihapus dari UI. Jangan mengandalkan waktu yang tepat ketika metode ini dijalankan. Misalnya, IAsyncDisposable dapat dipicu sebelum atau sesudah asinkron Task yang ditunggu dipanggil OnInitalizedAsync
atau selesai. Selain itu, kode pembuangan objek tidak boleh mengasumsikan bahwa objek yang dibuat selama inisialisasi atau metode siklus hidup lainnya ada.
Komponen tidak perlu diimplementasikan IDisposable dan IAsyncDisposable secara bersamaan. Jika keduanya diimplementasikan, kerangka kerja hanya menjalankan kelebihan beban asinkron.
Kode pengembang harus memastikan bahwa IAsyncDisposable implementasi tidak membutuhkan waktu lama untuk diselesaikan.
Pembuangan referensi objek interop JavaScript
Contoh di seluruh artikel interop JavaScript (JS) menunjukkan pola pembuangan objek umum:
Saat memanggil JS dari .NET, seperti yang dijelaskan dalam Memanggil fungsi JavaScript dari metode .NET di ASP.NET Core Blazor, buang apa pun yang dibuatIJSInProcessObjectReference//JSObjectReference IJSObjectReferencebaik dari .NET atau dari JS untuk menghindari kebocoran JS memori.
Saat memanggil .NET dari JS, seperti yang dijelaskan dalam Metode panggilan .NET dari fungsi JavaScript di ASP.NET CoreBlazor , buang yang dibuat DotNetObjectReference baik dari .NET atau dari JS untuk menghindari kebocoran memori .NET.
JS referensi objek interop diimplementasikan sebagai peta yang dikunci oleh pengidentifikasi di sisi JS panggilan interop yang membuat referensi. Ketika pembuangan objek dimulai dari .NET atau JS sisi, Blazor menghapus entri dari peta, dan objek dapat menjadi sampah yang dikumpulkan selama tidak ada referensi kuat lainnya ke objek yang ada.
Minimal, selalu buang objek yang dibuat di sisi .NET untuk menghindari kebocoran memori terkelola .NET.
Tugas pembersihan DOM selama pembuangan komponen
Untuk informasi selengkapnya, lihat ASP.NET Blazor interoperabilitas Core JavaScript (JS interop).
Untuk panduan tentang JSDisconnectedException kapan sirkuit terputus, lihat ASP.NET Blazor interoperabilitas Core JavaScript (JS interop). Untuk panduan penanganan kesalahan interop JavaScript umum, lihat bagian interop JavaScript di Menangani kesalahan di aplikasi ASP.NET CoreBlazor.
Sinkron IDisposable
Untuk tugas pembuangan sinkron, gunakan IDisposable.Dispose.
Komponen berikut:
- IDisposable Menerapkan dengan direktif
@implements
Razor. obj
Membuang , yang merupakan jenis yang mengimplementasikan IDisposable.- Pemeriksaan null dilakukan karena
obj
dibuat dalam metode siklus hidup (tidak ditampilkan).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Jika satu objek memerlukan pembuangan, lambda dapat digunakan untuk membuang objek ketika Dispose dipanggil. Contoh berikut muncul di artikel penyajian komponen ASP.NET Core Razor dan menunjukkan penggunaan ekspresi lambda untuk pembuangan Timer.
TimerDisposal1.razor
:
@page "/timer-disposal-1"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 1</PageTitle>
<h1>Timer Disposal Example 1</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
TimerDisposal1.razor
:
@page "/timer-disposal-1"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 1</PageTitle>
<h1>Timer Disposal Example 1</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor
:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor
:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor
:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
CounterWithTimerDisposal1.razor
:
@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer = new Timer(1000);
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
}
Catatan
Dalam contoh sebelumnya, panggilan ke StateHasChanged dibungkus oleh panggilan ke ComponentBase.InvokeAsync karena panggilan balik dipanggil di luar Blazorkonteks sinkronisasi . Untuk informasi lebih lanjut, lihat perenderan komponen Razor ASP.NET Core.
Jika objek dibuat dalam metode siklus hidup, seperti OnInitialized{Async}
, periksa null
sebelum memanggil Dispose
.
TimerDisposal2.razor
:
@page "/timer-disposal-2"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 2</PageTitle>
<h1>Timer Disposal Example 2</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
TimerDisposal2.razor
:
@page "/timer-disposal-2"
@using System.Timers
@implements IDisposable
<PageTitle>Timer Disposal 2</PageTitle>
<h1>Timer Disposal Example 2</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor
:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor
:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer? timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor
:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
CounterWithTimerDisposal2.razor
:
@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable
<h1>Counter with <code>Timer</code> disposal</h1>
<p>Current count: @currentCount</p>
@code {
private int currentCount = 0;
private Timer timer;
protected override void OnInitialized()
{
timer = new Timer(1000);
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
currentCount++;
StateHasChanged();
});
}
public void Dispose() => timer?.Dispose();
}
Untuk informasi selengkapnya, lihat:
Asynchronous IAsyncDisposable
Untuk tugas pembuangan asinkron, gunakan IAsyncDisposable.DisposeAsync.
Komponen berikut:
- IAsyncDisposable Menerapkan dengan direktif
@implements
Razor. obj
Membuang , yang merupakan jenis yang tidak dikelola yang mengimplementasikan IAsyncDisposable.- Pemeriksaan null dilakukan karena
obj
dibuat dalam metode siklus hidup (tidak ditampilkan).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Untuk informasi selengkapnya, lihat:
Penugasan null
ke objek yang dibuang
Biasanya, tidak perlu menetapkan null
ke objek yang dibuang setelah memanggil/DisposeDisposeAsync . Kasus langka untuk ditetapkan null
meliputi yang berikut ini:
- Jika jenis objek diimplementasikan dengan buruk dan tidak mentolerir panggilan berulang ke Dispose/DisposeAsync, tetapkan
null
setelah pembuangan untuk dengan anggun melewati panggilan lebih lanjut ke .Dispose/DisposeAsync - Jika proses berumur panjang terus memegang referensi ke objek yang dibuang, menetapkan
null
memungkinkan pengumpul sampah untuk membebaskan objek terlepas dari proses berumur panjang yang memegang referensi untuk itu.
Ini adalah skenario yang tidak biasa. Untuk objek yang diimplementasikan dengan benar dan bersifat normal, tidak ada gunanya menetapkan null
ke objek yang dibuang. Dalam kasus yang jarang terjadi di mana objek harus ditetapkan , sebaiknya dokumentasikan null
alasan dan mencari solusi yang mencegah kebutuhan untuk menetapkan null
.
StateHasChanged
Catatan
Memanggil StateHasChanged dan Dispose
DisposeAsync
tidak didukung. StateHasChanged mungkin dipanggil sebagai bagian dari merobek perender, jadi meminta pembaruan UI pada saat itu tidak didukung.
Penangan kejadian
Selalu berhenti berlangganan penanganan aktivitas dari peristiwa .NET. Contoh formulir berikut Blazor menunjukkan cara berhenti berlangganan penanganan aktivitas dalam Dispose
metode :
Bidang privat dan pendekatan lambda
@implements IDisposable <EditForm ... EditContext="editContext" ...> ... <button type="submit" disabled="@formInvalid">Submit</button> </EditForm> @code { ... private EventHandler<FieldChangedEventArgs>? fieldChanged; protected override void OnInitialized() { editContext = new(model); fieldChanged = (_, __) => { ... }; editContext.OnFieldChanged += fieldChanged; } public void Dispose() { editContext.OnFieldChanged -= fieldChanged; } }
Pendekatan metode privat
@implements IDisposable <EditForm ... EditContext="editContext" ...> ... <button type="submit" disabled="@formInvalid">Submit</button> </EditForm> @code { ... protected override void OnInitialized() { editContext = new(model); editContext.OnFieldChanged += HandleFieldChanged; } private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { ... } public void Dispose() { editContext.OnFieldChanged -= HandleFieldChanged; } }
Untuk informasi selengkapnya, lihat bagian Dan pembuangan IDisposable
IAsyncDisposable
komponen.
Untuk informasi selengkapnya tentang EditForm komponen dan formulir, lihat gambaran umum formulir ASP.NET Core Blazor dan artikel formulir lainnya di simpul Formulir.
Fungsi, metode, dan ekspresi anonim
Saat fungsi, metode, atau ekspresi anonim, digunakan, tidak perlu menerapkan IDisposable dan berhenti berlangganan delegasi. Namun, gagal berhenti berlangganan delegasi adalah masalah ketika objek yang mengekspos peristiwa keluar dari masa pakai komponen yang mendaftarkan delegasi. Ketika ini terjadi, kebocoran memori menghasilkan karena delegasi terdaftar menjaga objek asli tetap hidup. Oleh karena itu, hanya gunakan pendekatan berikut ketika Anda tahu bahwa acara mendelegasikan dibuang dengan cepat. Ketika ragu tentang masa pakai objek yang memerlukan pembuangan, berlangganan metode delegasi dan membuang delegasi dengan benar seperti yang ditunjukkan contoh sebelumnya.
Pendekatan metode lambda anonim (pembuangan eksplisit tidak diperlukan):
private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { formInvalid = !editContext.Validate(); StateHasChanged(); } protected override void OnInitialized() { editContext = new(starship); editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e); }
Pendekatan ekspresi lambda anonim (pembuangan eksplisit tidak diperlukan):
private ValidationMessageStore? messageStore; [CascadingParameter] private EditContext? CurrentEditContext { get; set; } protected override void OnInitialized() { ... messageStore = new(CurrentEditContext); CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear(); CurrentEditContext.OnFieldChanged += (s, e) => messageStore.Clear(e.FieldIdentifier); }
Contoh lengkap kode sebelumnya dengan ekspresi lambda anonim muncul di artikel validasi formulir ASP.NET CoreBlazor.
Untuk informasi selengkapnya, lihat Membersihkan sumber daya yang tidak dikelola dan topik yang mengikutinya tentang menerapkan Dispose
metode dan DisposeAsync
.
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 dimatikan untuk penyebaran 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:
- CancellationTokenSource Gunakan dan CancellationToken.
- Pada pembuangan komponen dan pada titik mana pun pembatalan diinginkan dengan membatalkan token secara manual, panggil
CancellationTokenSource.Cancel
untuk memberi sinyal bahwa pekerjaan latar belakang harus dibatalkan. - Setelah panggilan asinkron kembali, panggil ThrowIfCancellationRequested pada token.
Dalam contoh berikut:
await Task.Delay(5000, cts.Token);
mewakili pekerjaan 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;
}
}
}
Blazor Server peristiwa koneksi ulang
Peristiwa siklus hidup komponen yang tercakup dalam artikel ini beroperasi secara terpisah dari penanganan aktivitas 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 panduan ASP.NET CoreBlazorSignalR.
Sumber Daya Tambahan:
Menangani pengecualian yang tertangkap di luar Razor siklus hidup komponen
ASP.NET Core