ASP.NET Core Razor-Komponentenlebenszyklus
In diesem Artikel werden der ASP.NET Core Razor-Komponentenlebenszyklus und die Verwendung von Lebenszyklusereignissen erläutert.
Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.
Lebenszyklusereignisse
Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.
Ereignisse des Komponentenlebenszyklus:
- Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
- Erstellen Sie die Instanz der Komponente.
- Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus
SetParametersAsync
. - Rufen Sie
OnInitialized{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rufen Sie
OnParametersSet{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert. - Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Hinweis
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.
Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Verarbeitung von DOM-Ereignissen (Document Object Model):
- Der Ereignishandler wird ausgeführt.
- Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Der Render
-Lebenszyklus:
- Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
- Nach dem ersten Rendering
- Wenn
ShouldRender
false
ist.
- Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
- Warten Sie, bis das DOM aktualisiert wurde.
- Rufen Sie
OnAfterRender{Async}
auf.
Wenn StateHasChanged
von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase
-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.
Hinweis
Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).
Wenn Parameter festgelegt wurden (SetParametersAsync
)
SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.
Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.
Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]
- oder [CascadingParameter]
-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.
Wenn base.SetParametersAsync
nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param
-Parameters value
zu, wenn die Analyse eines Routingparameters für Param
erfolgreich ist. Wenn value
nicht null
lautet, wird der Wert von der Komponente angezeigt.
Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?}
in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?}
abzurufen. Wenn /{param?}
in diesem Szenario verwendet wird, gibt TryGetValuefalse
zurück, und message
wird auf keine der message
-Zeichenfolgen festgelegt.
Pages/SetParamsAsync.razor
:
@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);
}
}
Komponenteninitialisierung (OnInitialized{Async}
)
OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.
Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:
Pages/OnInit.razor
:
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await
, um einen asynchronen Vorgang durchzuführen:
protected override async Task OnInitializedAsync()
{
await ...
}
Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:
- Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
- Ein zweites Mal, wenn der Browser die Komponente rendert.
Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Nachdem Parameter festgelegt wurden (OnParametersSet{Async}
)
OnParametersSet oder OnParametersSetAsync wird aufgerufen:
Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.
Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:
- Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
- Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.
Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:
- Mit einem Startdatum, das von
StartDate
empfangen wird:/on-parameters-set/2021-03-19
- Ohne Startdatum, wobei
StartDate
einem Wert der aktuellen Ortszeit zugewiesen wird:/on-parameters-set
Pages/OnParamsSet.razor
:
Hinweis
In einer Komponentenroute ist es nicht möglich, einen DateTime-Parameter mit der Routenbeschränkung datetime
einzuschränken und den Parameter optional zu machen. Daher verwendet die folgende OnParamsSet
-Komponente zwei @page
-Anweisungen, um das Routing mit und ohne ein bereitgestelltes Datumssegment in der URL zu verarbeiten.
@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}).";
}
}
}
Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnParametersSetAsync()
{
await ...
}
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.
Nach dem Rendern der Komponente (OnAfterRender{Async}
)
OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.
Der Parameter firstRender
für OnAfterRender und OnAfterRenderAsync:
- Wird auf
true
festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird. - Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.
Pages/AfterRender.razor
:
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<button @onclick="LogInformation">Log information (and trigger a render)</button>
@code {
private string message = "Initial assigned message.";
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender(1): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
if (firstRender)
{
message = "Executed for the first render.";
}
else
{
message = "Executed after the first render.";
}
Logger.LogInformation("OnAfterRender(2): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
}
private void LogInformation()
{
Logger.LogInformation("LogInformation called");
}
}
Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await ...
}
}
Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.
OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendern interaktiv gerendert wird. Beim Vorabrendern der App:
- Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
- Wenn das Blazor-Skript (
blazor.server.js
oderblazor.webassembly.js
) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Statusänderungen (StateHasChanged
)
StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.
StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.
Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.
Behandeln unvollständiger asynchroner Aktionen beim Rendern
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null
oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null
sind.
In der FetchData
-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts
). Wenn forecasts
gleich null
ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task
abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.
Pages/FetchData.razor
in der Blazor Server-Vorlage:
@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<!-- forecast data in table element content -->
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
}
}
Behandeln von Fehlern
Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.
Zustandsbehaftete erneute Verbindung nach dem Prerendering
Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}
-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:
- Wenn die Komponente statisch vorab gerendert ist.
- Nachdem die Serververbindung hergestellt wurde.
Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.
Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService
-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)
) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync
-Methode zurückgegeben werden.
WeatherForecastService.cs
:
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)
});
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();
});
}
}
Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.
Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Voarabrendering mit JavaScript-Interop
Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.
Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).
Im folgenden Beispiel wird die Funktion setElementText1
im Element <head>
platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText1 = (element, text) => element.innerText = text;
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Das OnAfterRender{Async}
-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}
-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.
Pages/PrerenderedInterop1.razor
:
@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS
<div @ref="divElement">Text during render</div>
@code {
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync(
"setElementText1", divElement, "Text after render");
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText1 = (element, text) => element.innerText = text;
Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.
Im folgenden Beispiel wird die Funktion setElementText2
im Element <head>
platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.
StateHasChanged
wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged
nur aufgerufen wird, wenn data
null
ist.
Pages/PrerenderedInterop2.razor
:
@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<p>
Get value via JS interop call:
<strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>
<p>
Set value via JS interop call:
</p>
<div id="val-set-by-interop" @ref="divElement"></div>
@code {
private string? data;
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && data == null)
{
data = await JS.InvokeAsync<string>(
"setElementText2", divElement, "Hello from interop call!");
StateHasChanged();
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.
Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.
Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.
Löschen von Objektverweisen bei JavaScript-Interoperabilität
Alle Beispiele im Artikel zur JavaScript-Interoperabilität (JS) veranschaulichen typische Muster zum Löschen von Objekten:
Wenn Sie JS aus .NET aufrufen, wie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in ASP.NET Core Blazor beschrieben, löschen Sie alle erstellten IJSObjectReference/IJSInProcessObjectReference/
JSObjectReference
-Elemente entweder aus .NET oder aus JS, um JS-Arbeitsspeicherverluste zu vermeiden.Wenn Sie .NET aus JS aufrufen, wie unter Aufrufen von .NET-Methoden in JavaScript-Funktionen in ASP.NET Core Blazor beschrieben, löschen Sie alle erstellten DotNetObjectReference-Elemente entweder aus .NET oder aus JS, um .NET-Arbeitsspeicherverluste zu vermeiden.
Objektverweise bei JS-Interoperabilität werden als Zuordnung implementiert, die durch einen Bezeichner auf der Seite des JS-Interoperabilitätsaufrufs definiert ist, der den Verweis erstellt. Wenn die Objektlöschung von .NET oder JS aus initiiert wird, entfernt Blazor den Eintrag aus der Zuordnung, und das Objekt kann durch die Garbage Collection gelöscht werden, sofern kein anderer starker Verweis auf das Objekt vorhanden ist.
Sie sollten immer mindestens die auf .NET-Seite erstellten Objekte löschen, um Verluste im von .NET verwalteten Arbeitsspeicher zu vermeiden.
DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten
Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver
-Muster in JavaScript auf dem Client:
- Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in
Dispose{Async}
aus dem DOM entfernt. - In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in
Dispose{Async}
ausgeführt wird.
Mit dem Muster MutationObserver
können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.
Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.
Synchrone IDisposable
Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.
Pages/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();
}
Hinweis
Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized
/OnInitializedAsync
null
Dispose
erstellt wird.
Pages/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();
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Asynchrone IAsyncDisposable
Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IAsyncDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IAsyncDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Zuweisung von null
zu beseitigten Objekten
In der Regel ist es nicht notwendig, null
freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null
zugewiesen werden muss, gehören:
- Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie
null
nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen. - Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von
null
das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.
Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null
zuzuweisen. In den seltenen Fällen, in denen null
einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null
zugewiesen werden muss.
StateHasChanged
Hinweis
Der Aufruf von StateHasChanged in Dispose
wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.
Ereignishandler
Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose
-Methode:
Ansatz mit einem privatem Feld und Lambdaausdruck
@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; } }
Ansatz mit einer privaten Methode
@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; } }
Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
.
Anonyme Funktionen, Methoden und Ausdrücke
Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.
Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):
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); }
Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):
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); }
Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.
Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose
und DisposeAsync
.
Abbrechbare Hintergrundarbeit
Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.
Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:
- Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
- Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
- Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
- Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
- Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.
So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:
- Verwenden Sie eine CancellationTokenSource-Klasse und eine CancellationToken-Struktur.
- Bei der Entsorgung der Komponente und zu jedem beliebigen Zeitpunkt, an dem eine Kündigung durch manuelles Löschen des Tokens gewünscht wird, rufen Sie
CancellationTokenSource.Cancel
auf, um zu signalisieren, dass die Hintergrundarbeit abgebrochen werden soll. - Rufen Sie nach Rückgabe des asynchronen Aufrufs ThrowIfCancellationRequested für das Token auf.
Im folgenden Beispiel:
await Task.Delay(5000, cts.Token);
stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.BackgroundResourceMethod
stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn dieResource
vor dem Aufruf der Methode verworfen wird.
Pages/BackgroundWork.razor
:
@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;
}
}
}
Blazor Server-Ereignisse zur Wiederherstellung von Verbindungen
Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.
Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.
Lebenszyklusereignisse
Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.
Ereignisse des Komponentenlebenszyklus:
- Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
- Erstellen Sie die Instanz der Komponente.
- Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus
SetParametersAsync
. - Rufen Sie
OnInitialized{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rufen Sie
OnParametersSet{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert. - Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Hinweis
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.
Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Verarbeitung von DOM-Ereignissen (Document Object Model):
- Der Ereignishandler wird ausgeführt.
- Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Der Render
-Lebenszyklus:
- Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
- Nach dem ersten Rendering
- Wenn
ShouldRender
false
ist.
- Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
- Warten Sie, bis das DOM aktualisiert wurde.
- Rufen Sie
OnAfterRender{Async}
auf.
Wenn StateHasChanged
von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase
-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.
Hinweis
Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).
Wenn Parameter festgelegt wurden (SetParametersAsync
)
SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.
Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.
Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]
- oder [CascadingParameter]
-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.
Wenn base.SetParametersAsync
nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param
-Parameters value
zu, wenn die Analyse eines Routingparameters für Param
erfolgreich ist. Wenn value
nicht null
lautet, wird der Wert von der Komponente angezeigt.
Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?}
in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?}
abzurufen. Wenn /{param?}
in diesem Szenario verwendet wird, gibt TryGetValuefalse
zurück, und message
wird auf keine der message
-Zeichenfolgen festgelegt.
Pages/SetParamsAsync.razor
:
@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);
}
}
Komponenteninitialisierung (OnInitialized{Async}
)
OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.
Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:
Pages/OnInit.razor
:
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await
, um einen asynchronen Vorgang durchzuführen:
protected override async Task OnInitializedAsync()
{
await ...
}
Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:
- Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
- Ein zweites Mal, wenn der Browser die Komponente rendert.
Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Nachdem Parameter festgelegt wurden (OnParametersSet{Async}
)
OnParametersSet oder OnParametersSetAsync wird aufgerufen:
Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.
Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:
- Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
- Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.
Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:
- Mit einem Startdatum, das von
StartDate
empfangen wird:/on-parameters-set/2021-03-19
- Ohne Startdatum, wobei
StartDate
einem Wert der aktuellen Ortszeit zugewiesen wird:/on-parameters-set
Pages/OnParamsSet.razor
:
Hinweis
In einer Komponentenroute ist es nicht möglich, einen DateTime-Parameter mit der Routenbeschränkung datetime
einzuschränken und den Parameter optional zu machen. Daher verwendet die folgende OnParamsSet
-Komponente zwei @page
-Anweisungen, um das Routing mit und ohne ein bereitgestelltes Datumssegment in der URL zu verarbeiten.
@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}).";
}
}
}
Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnParametersSetAsync()
{
await ...
}
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.
Nach dem Rendern der Komponente (OnAfterRender{Async}
)
OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.
Der Parameter firstRender
für OnAfterRender und OnAfterRenderAsync:
- Wird auf
true
festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird. - Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.
Pages/AfterRender.razor
:
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<button @onclick="LogInformation">Log information (and trigger a render)</button>
@code {
private string message = "Initial assigned message.";
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender(1): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
if (firstRender)
{
message = "Executed for the first render.";
}
else
{
message = "Executed after the first render.";
}
Logger.LogInformation("OnAfterRender(2): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
}
private void LogInformation()
{
Logger.LogInformation("LogInformation called");
}
}
Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await ...
}
}
Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.
OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendering gerendert und interaktiv ist. Beim Vorabrendern der App:
- Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
- Wenn das Blazor-Skript (
blazor.server.js
oderblazor.webassembly.js
) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Statusänderungen (StateHasChanged
)
StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.
StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.
Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.
Behandeln unvollständiger asynchroner Aktionen beim Rendern
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null
oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null
sind.
In der FetchData
-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts
). Wenn forecasts
gleich null
ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task
abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.
Pages/FetchData.razor
in der Blazor Server-Vorlage:
@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<!-- forecast data in table element content -->
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}
Behandeln von Fehlern
Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.
Zustandsbehaftete erneute Verbindung nach dem Prerendering
Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}
-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:
- Wenn die Komponente statisch vorab gerendert ist.
- Nachdem die Serververbindung hergestellt wurde.
Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.
Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService
-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)
) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync
-Methode zurückgegeben werden.
WeatherForecastService.cs
:
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)
});
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();
});
}
}
Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.
Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Voarabrendering mit JavaScript-Interop
Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.
Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).
Im folgenden Beispiel wird die Funktion setElementText1
im Element <head>
platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText1 = (element, text) => element.innerText = text;
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Das OnAfterRender{Async}
-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}
-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.
Pages/PrerenderedInterop1.razor
:
@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS
<div @ref="divElement">Text during render</div>
@code {
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync(
"setElementText1", divElement, "Text after render");
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText1 = (element, text) => element.innerText = text;
Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.
Im folgenden Beispiel wird die Funktion setElementText2
im Element <head>
platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.
StateHasChanged
wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged
nur aufgerufen wird, wenn data
null
ist.
Pages/PrerenderedInterop2.razor
:
@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<p>
Get value via JS interop call:
<strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>
<p>
Set value via JS interop call:
</p>
<div id="val-set-by-interop" @ref="divElement"></div>
@code {
private string? data;
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && data == null)
{
data = await JS.InvokeAsync<string>(
"setElementText2", divElement, "Hello from interop call!");
StateHasChanged();
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.
Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.
Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.
DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten
Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver
-Muster in JavaScript auf dem Client:
- Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in
Dispose{Async}
aus dem DOM entfernt. - In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in
Dispose{Async}
ausgeführt wird.
Mit dem Muster MutationObserver
können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.
Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.
Synchrone IDisposable
Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.
Pages/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();
}
Hinweis
Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized
/OnInitializedAsync
null
Dispose
erstellt wird.
Pages/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();
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Asynchrone IAsyncDisposable
Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IAsyncDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IAsyncDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Zuweisung von null
zu beseitigten Objekten
In der Regel ist es nicht notwendig, null
freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null
zugewiesen werden muss, gehören:
- Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie
null
nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen. - Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von
null
das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.
Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null
zuzuweisen. In den seltenen Fällen, in denen null
einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null
zugewiesen werden muss.
StateHasChanged
Hinweis
Der Aufruf von StateHasChanged in Dispose
wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.
Ereignishandler
Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose
-Methode:
Ansatz mit einem privatem Feld und Lambdaausdruck
@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; } }
Ansatz mit einer privaten Methode
@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; } }
Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
.
Anonyme Funktionen, Methoden und Ausdrücke
Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.
Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):
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); }
Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):
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); }
Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.
Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose
und DisposeAsync
.
Abbrechbare Hintergrundarbeit
Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.
Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:
- Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
- Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
- Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
- Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
- Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.
So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:
- Verwenden Sie eine CancellationTokenSource-Klasse und eine CancellationToken-Struktur.
- Bei der Entsorgung der Komponente und zu jedem beliebigen Zeitpunkt, an dem eine Kündigung durch manuelles Löschen des Tokens gewünscht wird, rufen Sie
CancellationTokenSource.Cancel
auf, um zu signalisieren, dass die Hintergrundarbeit abgebrochen werden soll. - Rufen Sie nach Rückgabe des asynchronen Aufrufs ThrowIfCancellationRequested für das Token auf.
Im folgenden Beispiel:
await Task.Delay(5000, cts.Token);
stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.BackgroundResourceMethod
stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn dieResource
vor dem Aufruf der Methode verworfen wird.
Pages/BackgroundWork.razor
:
@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;
}
}
}
Blazor Server-Ereignisse zur Wiederherstellung von Verbindungen
Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.
Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.
Lebenszyklusereignisse
Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.
Ereignisse des Komponentenlebenszyklus:
- Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
- Erstellen Sie die Instanz der Komponente.
- Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus
SetParametersAsync
. - Rufen Sie
OnInitialized{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rufen Sie
OnParametersSet{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert. - Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Hinweis
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.
Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Verarbeitung von DOM-Ereignissen (Document Object Model):
- Der Ereignishandler wird ausgeführt.
- Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Der Render
-Lebenszyklus:
- Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
- Nach dem ersten Rendering
- Wenn
ShouldRender
false
ist.
- Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
- Warten Sie, bis das DOM aktualisiert wurde.
- Rufen Sie
OnAfterRender{Async}
auf.
Wenn StateHasChanged
von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase
-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.
Hinweis
Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).
Wenn Parameter festgelegt wurden (SetParametersAsync
)
SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.
Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.
Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]
- oder [CascadingParameter]
-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.
Wenn base.SetParametersAsync
nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param
-Parameters value
zu, wenn die Analyse eines Routingparameters für Param
erfolgreich ist. Wenn value
nicht null
lautet, wird der Wert von der Komponente angezeigt.
Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?}
in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?}
abzurufen. Wenn /{param?}
in diesem Szenario verwendet wird, gibt TryGetValuefalse
zurück, und message
wird auf keine der message
-Zeichenfolgen festgelegt.
Pages/SetParamsAsync.razor
:
@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);
}
}
Komponenteninitialisierung (OnInitialized{Async}
)
OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.
Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:
Pages/OnInit.razor
:
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await
, um einen asynchronen Vorgang durchzuführen:
protected override async Task OnInitializedAsync()
{
await ...
}
Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:
- Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
- Ein zweites Mal, wenn der Browser die Komponente rendert.
Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Nachdem Parameter festgelegt wurden (OnParametersSet{Async}
)
OnParametersSet oder OnParametersSetAsync wird aufgerufen:
Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.
Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:
- Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
- Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.
Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:
- Mit einem Startdatum, das von
StartDate
empfangen wird:/on-parameters-set/2021-03-19
- Ohne Startdatum, wobei
StartDate
einem Wert der aktuellen Ortszeit zugewiesen wird:/on-parameters-set
Pages/OnParamsSet.razor
:
Hinweis
In einer Komponentenroute ist es nicht möglich, einen DateTime-Parameter mit der Routenbeschränkung datetime
einzuschränken und den Parameter optional zu machen. Daher verwendet die folgende OnParamsSet
-Komponente zwei @page
-Anweisungen, um das Routing mit und ohne ein bereitgestelltes Datumssegment in der URL zu verarbeiten.
@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}).";
}
}
}
Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnParametersSetAsync()
{
await ...
}
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.
Nach dem Rendern der Komponente (OnAfterRender{Async}
)
OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.
Der Parameter firstRender
für OnAfterRender und OnAfterRenderAsync:
- Wird auf
true
festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird. - Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.
Pages/AfterRender.razor
:
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<button @onclick="LogInformation">Log information (and trigger a render)</button>
@code {
private string message = "Initial assigned message.";
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender(1): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
if (firstRender)
{
message = "Executed for the first render.";
}
else
{
message = "Executed after the first render.";
}
Logger.LogInformation("OnAfterRender(2): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
}
private void LogInformation()
{
Logger.LogInformation("LogInformation called");
}
}
Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await ...
}
}
Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.
OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendering gerendert und interaktiv ist. Beim Vorabrendern der App:
- Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
- Wenn das Blazor-Skript (
blazor.server.js
oderblazor.webassembly.js
) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Statusänderungen (StateHasChanged
)
StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.
StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.
Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.
Behandeln unvollständiger asynchroner Aktionen beim Rendern
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null
oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null
sind.
In der FetchData
-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts
). Wenn forecasts
gleich null
ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task
abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.
Pages/FetchData.razor
in der Blazor Server-Vorlage:
@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<!-- forecast data in table element content -->
</table>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}
Behandeln von Fehlern
Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.
Zustandsbehaftete erneute Verbindung nach dem Prerendering
Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}
-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:
- Wenn die Komponente statisch vorab gerendert ist.
- Nachdem die Serververbindung hergestellt wurde.
Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.
Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService
-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)
) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync
-Methode zurückgegeben werden.
WeatherForecastService.cs
:
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();
});
}
}
Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.
Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Voarabrendering mit JavaScript-Interop
Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.
Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).
Im folgenden Beispiel wird die Funktion setElementText1
im Element <head>
platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText1 = (element, text) => element.innerText = text;
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Das OnAfterRender{Async}
-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}
-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.
Pages/PrerenderedInterop1.razor
:
@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS
<div @ref="divElement">Text during render</div>
@code {
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync(
"setElementText1", divElement, "Text after render");
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText1 = (element, text) => element.innerText = text;
Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.
Im folgenden Beispiel wird die Funktion setElementText2
im Element <head>
platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.
StateHasChanged
wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged
nur aufgerufen wird, wenn data
null
ist.
Pages/PrerenderedInterop2.razor
:
@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<p>
Get value via JS interop call:
<strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>
<p>
Set value via JS interop call:
</p>
<div id="val-set-by-interop" @ref="divElement"></div>
@code {
private string? data;
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && data == null)
{
data = await JS.InvokeAsync<string>(
"setElementText2", divElement, "Hello from interop call!");
StateHasChanged();
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.
Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.
Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.
DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten
Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver
-Muster in JavaScript auf dem Client:
- Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in
Dispose{Async}
aus dem DOM entfernt. - In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in
Dispose{Async}
ausgeführt wird.
Mit dem Muster MutationObserver
können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.
Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.
Synchrone IDisposable
Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.
Pages/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();
}
Hinweis
Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized
/OnInitializedAsync
null
Dispose
erstellt wird.
Pages/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();
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Asynchrone IAsyncDisposable
Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IAsyncDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IAsyncDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Zuweisung von null
zu beseitigten Objekten
In der Regel ist es nicht notwendig, null
freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null
zugewiesen werden muss, gehören:
- Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie
null
nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen. - Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von
null
das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.
Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null
zuzuweisen. In den seltenen Fällen, in denen null
einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null
zugewiesen werden muss.
StateHasChanged
Hinweis
Der Aufruf von StateHasChanged in Dispose
wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.
Ereignishandler
Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose
-Methode:
Ansatz mit einem privatem Feld und Lambdaausdruck
@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; } }
Ansatz mit einer privaten Methode
@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; } }
Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
.
Anonyme Funktionen, Methoden und Ausdrücke
Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.
Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):
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); }
Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):
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); }
Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.
Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose
und DisposeAsync
.
Abbrechbare Hintergrundarbeit
Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.
Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:
- Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
- Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
- Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
- Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
- Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.
So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:
- Verwenden Sie eine CancellationTokenSource-Klasse und eine CancellationToken-Struktur.
- Bei der Entsorgung der Komponente und zu jedem beliebigen Zeitpunkt, an dem eine Kündigung durch manuelles Löschen des Tokens gewünscht wird, rufen Sie
CancellationTokenSource.Cancel
auf, um zu signalisieren, dass die Hintergrundarbeit abgebrochen werden soll. - Rufen Sie nach Rückgabe des asynchronen Aufrufs ThrowIfCancellationRequested für das Token auf.
Im folgenden Beispiel:
await Task.Delay(5000, cts.Token);
stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.BackgroundResourceMethod
stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn dieResource
vor dem Aufruf der Methode verworfen wird.
Pages/BackgroundWork.razor
:
@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;
}
}
}
Blazor Server-Ereignisse zur Wiederherstellung von Verbindungen
Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.
Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.
Lebenszyklusereignisse
Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.
Ereignisse des Komponentenlebenszyklus:
- Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
- Erstellen Sie die Instanz der Komponente.
- Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus
SetParametersAsync
. - Rufen Sie
OnInitialized{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rufen Sie
OnParametersSet{Async}
auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert. - Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Hinweis
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.
Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Verarbeitung von DOM-Ereignissen (Document Object Model):
- Der Ereignishandler wird ausgeführt.
- Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
- Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Der Render
-Lebenszyklus:
- Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
- Nach dem ersten Rendering
- Wenn
ShouldRender
false
ist.
- Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
- Warten Sie, bis das DOM aktualisiert wurde.
- Rufen Sie
OnAfterRender{Async}
auf.
Wenn StateHasChanged
von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase
-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.
Hinweis
Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).
Wenn Parameter festgelegt wurden (SetParametersAsync
)
SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.
Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.
Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]
- oder [CascadingParameter]
-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.
Wenn base.SetParametersAsync
nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param
-Parameters value
zu, wenn die Analyse eines Routingparameters für Param
erfolgreich ist. Wenn value
nicht null
lautet, wird der Wert von der Komponente angezeigt.
Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?}
in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?}
abzurufen. Wenn /{param?}
in diesem Szenario verwendet wird, gibt TryGetValuefalse
zurück, und message
wird auf keine der message
-Zeichenfolgen festgelegt.
Pages/SetParamsAsync.razor
:
@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);
}
}
Komponenteninitialisierung (OnInitialized{Async}
)
OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.
Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.
Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:
Pages/OnInit.razor
:
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await
, um einen asynchronen Vorgang durchzuführen:
protected override async Task OnInitializedAsync()
{
await ...
}
Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:
- Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
- Ein zweites Mal, wenn der Browser die Komponente rendert.
Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Nachdem Parameter festgelegt wurden (OnParametersSet{Async}
)
OnParametersSet oder OnParametersSetAsync wird aufgerufen:
Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.
Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:
- Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
- Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.
Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:
- Mit einem Startdatum, das von
StartDate
empfangen wird:/on-parameters-set/2021-03-19
- Ohne Startdatum, wobei
StartDate
einem Wert der aktuellen Ortszeit zugewiesen wird:/on-parameters-set
Pages/OnParamsSet.razor
:
@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}).";
}
}
}
Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnParametersSetAsync()
{
await ...
}
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.
Nach dem Rendern der Komponente (OnAfterRender{Async}
)
OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.
Der Parameter firstRender
für OnAfterRender und OnAfterRenderAsync:
- Wird auf
true
festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird. - Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.
Pages/AfterRender.razor
:
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<button @onclick="LogInformation">Log information (and trigger a render)</button>
@code {
private string message = "Initial assigned message.";
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender(1): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
if (firstRender)
{
message = "Executed for the first render.";
}
else
{
message = "Executed after the first render.";
}
Logger.LogInformation("OnAfterRender(2): firstRender: " +
"{FirstRender}, message: {Message}", firstRender, message);
}
private void LogInformation()
{
Logger.LogInformation("LogInformation called");
}
}
Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await ...
}
}
Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.
OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendering gerendert und interaktiv ist. Beim Vorabrendern der App:
- Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
- Wenn das Blazor-Skript (
blazor.server.js
oderblazor.webassembly.js
) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposable
IAsyncDisposable
.
Statusänderungen (StateHasChanged
)
StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.
StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.
Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.
Behandeln unvollständiger asynchroner Aktionen beim Rendern
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null
oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null
sind.
In der FetchData
-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts
). Wenn forecasts
gleich null
ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task
abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.
Pages/FetchData.razor
in der Blazor Server-Vorlage:
@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<!-- forecast data in table element content -->
</table>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}
Behandeln von Fehlern
Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.
Zustandsbehaftete erneute Verbindung nach dem Prerendering
Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}
-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:
- Wenn die Komponente statisch vorab gerendert ist.
- Nachdem die Serververbindung hergestellt wurde.
Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.
Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService
-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)
) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync
-Methode zurückgegeben werden.
WeatherForecastService.cs
:
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();
});
}
}
Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.
Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.
Voarabrendering mit JavaScript-Interop
Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.
Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).
Im folgenden Beispiel wird die Funktion setElementText1
im Element <head>
platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText1 = (element, text) => element.innerText = text;
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Das OnAfterRender{Async}
-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}
-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.
Pages/PrerenderedInterop1.razor
:
@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS
<div @ref="divElement">Text during render</div>
@code {
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync(
"setElementText1", divElement, "Text after render");
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText1 = (element, text) => element.innerText = text;
Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.
Im folgenden Beispiel wird die Funktion setElementText2
im Element <head>
platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.
Hinweis
Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).
<script>
window.setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
</script>
Warnung
Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).
Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.
StateHasChanged
wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged
nur aufgerufen wird, wenn data
null
ist.
Pages/PrerenderedInterop2.razor
:
@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<p>
Get value via JS interop call:
<strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>
<p>
Set value via JS interop call:
</p>
<div id="val-set-by-interop" @ref="divElement"></div>
@code {
private string? data;
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && data == null)
{
data = await JS.InvokeAsync<string>(
"setElementText2", divElement, "Hello from interop call!");
StateHasChanged();
}
}
}
Hinweis
Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
Beispiel:
export setElementText2 = (element, text) => {
element.innerText = text;
return text;
};
Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.
Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.
Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.
DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten
Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver
-Muster in JavaScript auf dem Client:
- Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in
Dispose{Async}
aus dem DOM entfernt. - In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in
Dispose{Async}
ausgeführt wird.
Mit dem Muster MutationObserver
können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.
Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.
Synchrone IDisposable
Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable
...
@code {
...
public void Dispose()
{
obj?.Dispose();
}
}
Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.
Pages/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();
}
Hinweis
Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized
/OnInitializedAsync
null
Dispose
erstellt wird.
Pages/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();
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Asynchrone IAsyncDisposable
Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.
Die folgende Komponente führt folgende Aktionen aus:
- Implementiert IAsyncDisposable mit der
@implements
Razor-Direktive. - Beseitigt
obj
(ein nicht verwalteter Typ, der IAsyncDisposable implementiert). - Eine NULL-Überprüfung wird ausgeführt, da
obj
in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable
...
@code {
...
public async ValueTask DisposeAsync()
{
if (obj is not null)
{
await obj.DisposeAsync();
}
}
}
Weitere Informationen finden Sie unter
- Bereinigen von nicht verwalteten Ressourcen (.NET-Dokumentation)
- NULL-bedingte Operatoren „?.“ und „?[]“
Zuweisung von null
zu beseitigten Objekten
In der Regel ist es nicht notwendig, null
freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null
zugewiesen werden muss, gehören:
- Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie
null
nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen. - Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von
null
das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.
Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null
zuzuweisen. In den seltenen Fällen, in denen null
einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null
zugewiesen werden muss.
StateHasChanged
Hinweis
Der Aufruf von StateHasChanged in Dispose
wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.
Ereignishandler
Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose
-Methode:
Ansatz mit einem privatem Feld und Lambdaausdruck
@implements IDisposable <EditForm EditContext="@editContext"> ... <button type="submit" disabled="@formInvalid">Submit</button> </EditForm> @code { // ... private EventHandler<FieldChangedEventArgs> fieldChanged; protected override void OnInitialized() { editContext = new EditContext(model); fieldChanged = (_, __) => { // ... }; editContext.OnFieldChanged += fieldChanged; } public void Dispose() { editContext.OnFieldChanged -= fieldChanged; } }
Ansatz mit einer privaten Methode
@implements IDisposable <EditForm EditContext="@editContext"> ... <button type="submit" disabled="@formInvalid">Submit</button> </EditForm> @code { // ... protected override void OnInitialized() { editContext = new EditContext(model); editContext.OnFieldChanged += HandleFieldChanged; } private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { // ... } public void Dispose() { editContext.OnFieldChanged -= HandleFieldChanged; } }
Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable
und IAsyncDisposable
.
Anonyme Funktionen, Methoden und Ausdrücke
Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.
Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):
private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { formInvalid = !editContext.Validate(); StateHasChanged(); } protected override void OnInitialized() { editContext = new EditContext(starship); editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e); }
Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):
private ValidationMessageStore messageStore; [CascadingParameter] private EditContext CurrentEditContext { get; set; } protected override void OnInitialized() { ... messageStore = new ValidationMessageStore(CurrentEditContext); CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear(); CurrentEditContext.OnFieldChanged += (s, e) => messageStore.Clear(e.FieldIdentifier); }
Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.
Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose
und DisposeAsync
.
Abbrechbare Hintergrundarbeit
Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.
Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:
- Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
- Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
- Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
- Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
- Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.
So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:
- Verwenden Sie eine CancellationTokenSource-Klasse und eine CancellationToken-Struktur.
- Bei der Entsorgung der Komponente und zu jedem beliebigen Zeitpunkt, an dem eine Kündigung durch manuelles Löschen des Tokens gewünscht wird, rufen Sie
CancellationTokenSource.Cancel
auf, um zu signalisieren, dass die Hintergrundarbeit abgebrochen werden soll. - Rufen Sie nach Rückgabe des asynchronen Aufrufs ThrowIfCancellationRequested für das Token auf.
Im folgenden Beispiel:
await Task.Delay(5000, cts.Token);
stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.BackgroundResourceMethod
stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn dieResource
vor dem Aufruf der Methode verworfen wird.
Pages/BackgroundWork.razor
:
@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-Ereignisse zur Wiederherstellung von Verbindungen
Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.