Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.
Warnung
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
In diesem Artikel werden der ASP.NET Core Razor-Komponentenlebenszyklus und die Verwendung von Lebenszyklusereignissen erläutert.
Lebenszyklusereignisse
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.
Dieser Artikel vereinfacht die Verarbeitung von Komponentenlebenszyklusereignissen, um die komplexe Frameworklogik zu verdeutlichen, und deckt nicht alle Änderungen ab, die über die Jahre vorgenommen wurden. 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.
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)).
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.
- 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. Die synchrone Methode wird vor der asychronen Methode aufgerufen.
- 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. Die synchrone Methode wird vor der asychronen Methode aufgerufen. - Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.
Hinweis
Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, können das Rendern von Komponenten oder das Anzeigen von Daten verzögern. Weitere Informationen finden Sie im Abschnitt Behandeln Sie unvollständige asynchrone 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 eine synchrone Initialisierung der übergeordneten Komponente verwendet wird, ist garantiert, dass die Initialisierung der übergeordneten Komponente zuerst abgeschlossen wird. Wenn die asynchrone Initialisierung der Elternkomponente verwendet wird, lässt sich die Reihenfolge des Abschlusses der Initialisierung von Eltern- und Kindkomponenten nicht bestimmen, da sie vom Ablauf des Initialisierungscodes abhängt.
DOM-Ereignisverarbeitung:
- 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, wenn beide der folgenden Bedingungen erfüllt sind:
- Es ist nicht das erste Rendern.
-
ShouldRendergibtfalsezurück.
- Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
- Warten Sie, bis das DOM aktualisiert wurde.
- Rufen Sie
OnAfterRender{Async}auf. Die synchrone Methode wird vor der asychronen Methode aufgerufen.
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.
Quieszenz während der Vorrenderung
In serverseitigen Blazor Anwendungen wartet das Prerendering auf Ruhe, was bedeutet, dass eine Komponente erst dann gerendert wird, wenn alle Komponenten im Renderbaum fertig gerendert sind. Ruhe kann zu spürbaren Verzögerungen beim Rendern führen, wenn eine Komponente während der Initialisierung und anderer Lebenszyklusmethoden lang andauernde Aufgaben ausführt, was zu einer schlechten Benutzererfahrung führt. Weitere Informationen finden Sie im Abschnitt Behandeln Sie unvollständige asynchrone Aktionen beim Rendern weiter unten in diesem Artikel.
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 den Satz von SetParametersAsync 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.
Im Allgemeinen sollte ihr Code die Basisklassenmethode (await base.SetParametersAsync(parameters);) aufrufen, wenn SetParametersAsync überschrieben wird. In erweiterten Szenarien kann Entwicklercode die Werte der eingehenden Parameter auf beliebige Weise interpretieren, die das Aufrufen der Basisklassenmethode nicht erfordert. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen. Sie müssen jedoch auf die ComponentBase-Referenzquelle verweisen, wenn Sie den Code strukturieren, ohne die Basisklassenmethode aufzurufen, da sie andere Lebenszyklusmethoden aufruft und das Rendern in komplexer Weise auslöst.
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 Sie die Initialisierungs- und Rendering-API von ComponentBase.SetParametersAsync nutzen, aber keine eingehenden Parameter verarbeiten möchten, haben Sie die Möglichkeit, eine leere ParameterView an die Basisklassenmethode zu übergeben:
await base.SetParametersAsync(ParameterView.Empty);
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie unter ASP.NET Core Razor Komponentenentsorgung.
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.
SetParamsAsync.razor:
@page "/set-params-async/{Param?}"
<PageTitle>Set Parameters Async</PageTitle>
<h1>Set Parameters Async Example</h1>
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<PageTitle>Set Parameters Async</PageTitle>
<h1>Set Parameters Async Example</h1>
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string? Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async/{Param?}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
@page "/set-params-async"
@page "/set-params-async/{Param}"
<p>@message</p>
@code {
private string message = "Not set";
[Parameter]
public string Param { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.TryGetValue<string>(nameof(Param), out var value))
{
if (value is null)
{
message = "The value of 'Param' is null.";
}
else
{
message = $"The value of 'Param' is {value}.";
}
}
await base.SetParametersAsync(parameters);
}
}
Komponenteninitialisierung (OnInitialized{Async})
OnInitialized und OnInitializedAsync werden ausschließlich verwendet, um eine Komponente für die gesamte Lebensdauer der Komponenteninstanz zu initialisieren. Parameterwerte und Parameterwertänderungen sollten sich nicht auf die Initialisierung auswirken, die in diesen Methoden ausgeführt wird. Beispielsweise wird das Laden statischer Optionen in eine Dropdownliste, die sich während der Lebensdauer der Komponente nicht ändert und nicht von Parameterwerten abhängig ist, in einer dieser Lebenszyklusmethoden ausgeführt. Wenn Parameterwerte oder Änderungen in Parameterwerten sich auf den Komponentenzustand auswirken, verwenden Sie stattdessen OnParametersSet{Async}.
Diese Methoden werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat. Die synchrone Methode wird vor der asychronen Methode aufgerufen.
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 Elternkomponente verwendet wird, lässt sich die Reihenfolge des Abschlusses der Initialisierung von Eltern- und Kindkomponenten nicht bestimmen, da sie vom Ablauf des Initialisierungscodes abhängt.
Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:
OnInit.razor:
@page "/on-init"
<PageTitle>On Initialized</PageTitle>
<h1>On Initialized Example</h1>
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized() =>
message = $"Initialized at {DateTime.Now}";
}
@page "/on-init"
<PageTitle>On Initialized</PageTitle>
<h1>On Initialized Example</h1>
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized() =>
message = $"Initialized at {DateTime.Now}";
}
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string? message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
@page "/on-init"
<p>@message</p>
@code {
private string message;
protected override void OnInitialized()
{
message = $"Initialized at {DateTime.Now}";
}
}
Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await, um einen asynchronen Vorgang durchzuführen:
protected override async Task OnInitializedAsync()
{
await ...
}
Wenn eine benutzerdefinierte Basisklasse mit benutzerdefinierter Initialisierungslogik verwendet wird, rufen Sie OnInitializedAsync auf der Basisklasse auf:
protected override async Task OnInitializedAsync()
{
await ...
await base.OnInitializedAsync();
}
Es ist nicht erforderlich, ComponentBase.OnInitializedAsync aufzurufen, außer wenn eine benutzerdefinierte Basisklasse mit benutzerdefinierter Logik verwendet wird. Weitere Informationen finden Sie im Abschnitt Lebenszyklusmethoden der Basisklasse.
Eine Komponente muss sicherstellen, dass sie sich in einem gültigen Zustand für das Rendern befindet, wenn OnInitializedAsync auf eine potenziell unvollständige Taskwartet. Wenn die Methode eine unvollständige Taskzurückgibt, muss der Teil der Methode, der synchron abgeschlossen wird, die Komponente in einem für das Rendern gültigen Zustand belassen. Weitere Informationen finden Sie in den einführenden Anmerkungen zu ASP.NET Core Blazor Synchronisierungskontext und ASP.NET Core Razor Komponenten-Entsorgung.
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. Der Inhalt dieses Abschnitts konzentriert sich auf Blazor Web Apps und zustandsbehaftete SignalRWiederanschlüsse. Informationen zum Beibehalten des Zustands während der Ausführung des Initialisierungscodes während der Vorrenderung finden Sie unter ASP.NET Core Blazor vorrenderter Zustandsbeständigkeit.
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 im Abschnitt auf Blazor Server und die zustandsbehaftete SignalR-Verbindungswiederherstellung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Lösungen (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 der Vorrenderung finden Sie unter Integrieren ASP.NET Core-Komponenten Razor in MVC oder Razor Pages.
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 Prerendering mit JavaScript-Interop.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie unter ASP.NET Core Razor Komponentenentsorgung.
Verwenden Sie Streamingrendering mit statischem serverseitigem Rendering (SSR) oder Vorabrendering, um die Benutzererfahrung für Komponenten zu verbessern, die asynchrone Aufgaben mit langer Ausführungszeit in OnInitializedAsync ausführen, bis sie vollständig gerendert worden sind. Weitere Informationen finden Sie in den folgenden Ressourcen:
Nachdem Parameter festgelegt wurden (OnParametersSet{Async})
OnParametersSet oder OnParametersSetAsync werden 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.
Die synchrone Methode wird vor der asychronen Methode aufgerufen.
Die Methoden können auch dann aufgerufen werden, wenn sich die Parameterwerte nicht geändert haben. Dieses Verhalten unterstreicht die Notwendigkeit, dass Entwickler zusätzliche Logik in den Methoden implementieren müssen, um zu überprüfen, ob sich die Parameterwerte tatsächlich geändert haben, bevor von diesen Parametern abhängige Daten oder Zustände neu initialisiert werden.
Navigieren Sie für die folgende Beispielkomponente zur Seite der Komponente unter einer URL:
- Mit einem Startdatum, das von
StartDateempfangen wird:/on-parameters-set/2021-03-19 - Ohne Startdatum, wobei
StartDateeinem Wert der aktuellen Ortszeit zugewiesen wird:/on-parameters-set
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.
OnParamsSet.razor:
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<PageTitle>On Parameters Set</PageTitle>
<h1>On Parameters Set Example</h1>
<p>
Pass a datetime in the URI of the browser's address bar.
For example, add <code>/1-1-2024</code> to the address.
</p>
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied " +
$"(StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used " +
$"(StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<PageTitle>On Parameters Set</PageTitle>
<h1>On Parameters Set Example</h1>
<p>
Pass a datetime in the URI of the browser's address bar.
For example, add <code>/1-1-2024</code> to the address.
</p>
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied " +
$"(StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used " +
$"(StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string? message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"
<p>@message</p>
@code {
private string message;
[Parameter]
public DateTime StartDate { get; set; }
protected override void OnParametersSet()
{
if (StartDate == default)
{
StartDate = DateTime.Now;
message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
}
else
{
message = $"The start date in the URL was used (StartDate: {StartDate}).";
}
}
}
Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnParametersSetAsync()
{
await ...
}
Wenn eine benutzerdefinierte Basisklasse mit benutzerdefinierter Initialisierungslogik verwendet wird, rufen Sie OnParametersSetAsync auf der Basisklasse auf:
protected override async Task OnParametersSetAsync()
{
await ...
await base.OnParametersSetAsync();
}
Es ist nicht erforderlich, ComponentBase.OnParametersSetAsync aufzurufen, außer wenn eine benutzerdefinierte Basisklasse mit benutzerdefinierter Logik verwendet wird. Weitere Informationen finden Sie im Abschnitt Lebenszyklusmethoden der Basisklasse.
Eine Komponente muss sicherstellen, dass sie sich in einem gültigen Zustand für das Rendern befindet, wenn OnParametersSetAsync auf eine potenziell unvollständige Taskwartet. Wenn die Methode eine unvollständige Taskzurückgibt, muss der Teil der Methode, der synchron abgeschlossen wird, die Komponente in einem für das Rendern gültigen Zustand belassen. Weitere Informationen finden Sie in den einführenden Anmerkungen zu ASP.NET Core Blazor Synchronisierungskontext und ASP.NET Core Razor Komponenten-Entsorgung.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie unter ASP.NET Core Razor Komponentenentsorgung.
Wenn eine entsorgbare Komponente keine CancellationTokennutzt, sollten OnParametersSet und OnParametersSetAsync überprüfen, ob die Komponente entsorgt wurde. Wenn OnParametersSetAsync einen unvollständigen Taskzurückgibt, muss die Komponente sicherstellen, dass der Teil der Methode, der synchron abgeschlossen wird, die Komponente in einem gültigen Zustand für das Rendern belässt. Weitere Informationen finden Sie in den einführenden Anmerkungen zu ASP.NET Core Blazor Synchronisierungskontext und ASP.NET Core Razor Komponenten-Entsorgung.
Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core Blazor routing und navigation.
Ein Beispiel zur manuellen Implementierung von SetParametersAsync zur Verbesserung der Leistung in einigen Szenarien finden Sie unter ASP.NET Core bewährte Methoden für die RenderingleistungBlazor.
Nach dem Rendern der Komponente (OnAfterRender{Async})
OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente interaktiv gerendert und die Benutzeroberfläche aktualisiert wurde (z. B. nach dem Hinzufügen von Elementen zum Browser-DOM). 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. Die synchrone Methode wird vor der asychronen Methode aufgerufen.
Diese Methoden werden während des Vorab- oder statischen serverseitigen Renderings (SSR) auf dem Server nicht aufgerufen, weil diese Prozesse nicht an ein Livebrowser-DOM angefügt und bereits abgeschlossen sind, bevor das DOM aktualisiert wird.
Bei OnAfterRenderAsync wird die Komponente nach Abschluss aller zurückgegebenen Tasks nicht automatisch erneut gerendert, um eine endlose Renderschleife zu vermeiden.
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. Die synchrone Methode wird vor der asychronen Methode aufgerufen.
Diese Methoden werden während des Vorabrenderings nicht aufgerufen, da das Vorabrendering nicht an ein Livebrowser-DOM angefügt und bereits abgeschlossen ist, bevor das DOM aktualisiert wird.
Bei OnAfterRenderAsync wird die Komponente nach Abschluss aller zurückgegebenen Tasks nicht automatisch erneut gerendert, um eine endlose Renderschleife zu vermeiden.
Der Parameter firstRender für OnAfterRender und OnAfterRenderAsync:
- Wird auf
truefestgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird. - Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.
AfterRender.razor:
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender) =>
Logger.LogInformation("firstRender = {FirstRender}", firstRender);
private void HandleClick() => Logger.LogInformation("HandleClick called");
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender) =>
Logger.LogInformation("firstRender = {FirstRender}", firstRender);
private void HandleClick() => Logger.LogInformation("HandleClick called");
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@inject ILogger<AfterRender> Logger
<PageTitle>After Render</PageTitle>
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger
<h1>After Render Example</h1>
<p>
<button @onclick="HandleClick">Log information (and trigger a render)</button>
</p>
<p>Study logged messages in the console.</p>
@code {
protected override void OnAfterRender(bool firstRender)
{
Logger.LogInformation("OnAfterRender: firstRender = {FirstRender}", firstRender);
}
private void HandleClick()
{
Logger.LogInformation("HandleClick called");
}
}
Im Beispiel AfterRender.razor wird die folgende Ausgabe auf der Konsole erzeugt, wenn die Seite geladen und die Schaltfläche ausgewählt wird:
OnAfterRender: firstRender = True
HandleClick called
OnAfterRender: firstRender = False
Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
}
Wenn eine benutzerdefinierte Basisklasse mit benutzerdefinierter Initialisierungslogik verwendet wird, rufen Sie OnAfterRenderAsync auf der Basisklasse auf:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
...
await base.OnAfterRenderAsync(firstRender);
}
Es ist nicht erforderlich, ComponentBase.OnAfterRenderAsync aufzurufen, außer wenn eine benutzerdefinierte Basisklasse mit benutzerdefinierter Logik verwendet wird. Weitere Informationen finden Sie im Abschnitt Lebenszyklusmethoden der Basisklasse.
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 Vorabrendern 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|webassembly|web}.js) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werden OnAfterRender und OnAfterRenderAsyncaufgerufen, da sich die App nicht mehr in der Phase des Vorabrenderns befindet.
Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie unter ASP.NET Core Razor Komponentenentsorgung.
Lebenszyklusmethoden der Basisklasse
Beim Überschreiben der Lebenszyklusmethoden von Blazor ist es nicht erforderlich, Lebenszyklusmethoden der Basisklasse für ComponentBase aufzurufen. Eine Komponente sollte jedoch in den folgenden Situationen eine überschriebene Basisklassenlebenszyklusmethode aufrufen:
- Beim Überschreiben von ComponentBase.SetParametersAsync wird in der Regel
await base.SetParametersAsync(parameters);aufgerufen, da die Basisklassenmethode andere Lebenszyklusmethoden aufruft und das Rendern in komplexer Weise auslöst. Weitere Informationen finden Sie im Abschnitt Wenn Parameter festgelegt sind (SetParametersAsync). - Wenn die Basisklassenmethode Logik enthält, die ausgeführt werden muss. Bibliotheksconsumer rufen in der Regel Lebenszyklusmethoden der Basisklasse auf, wenn sie eine Basisklasse erben, da Bibliotheksbasisklassen häufig benutzerdefinierte Lebenszykluslogik zum Ausführen haben. Wenn die App eine Basisklasse aus einer Bibliothek verwendet, lesen Sie die Dokumentation der Bibliothek für Anleitung.
Im folgenden Beispiel wird base.OnInitialized(); aufgerufen, um sicherzustellen, dass die OnInitialized-Methode der Basisklasse ausgeführt wird. Ohne den Aufruf wird BlazorRocksBase2.OnInitialized nicht ausgeführt.
BlazorRocks2.razor:
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<PageTitle>Blazor Rocks!</PageTitle>
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@using Microsoft.Extensions.Logging
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
@page "/blazor-rocks-2"
@using Microsoft.Extensions.Logging
@inherits BlazorRocksBase2
@inject ILogger<BlazorRocks2> Logger
<h1>Blazor Rocks! Example 2</h1>
<p>
@BlazorRocksText
</p>
@code {
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
base.OnInitialized();
}
}
BlazorRocksBase2.cs:
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
protected override void OnInitialized() =>
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
protected override void OnInitialized() =>
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
namespace BlazorSample;
public class BlazorRocksBase2 : ComponentBase
{
[Inject]
private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
protected override void OnInitialized()
{
Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
}
}
Statusänderungen (StateHasChanged)
StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Falls zutreffend, wird durch den Aufruf von StateHasChanged eine Neuberechnung in die Warteschlange gestellt, die erfolgt, wenn der Hauptthread der Anwendung frei ist.
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 wann Sie StateHasChanged aufrufen sollten, einschließlich, wann man es mit ComponentBase.InvokeAsync aufruft, finden Sie unter ASP.NET Core Razor-Komponentenrendering.
Unvollständige asynchrone Aktionen beim Rendern behandeln
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 sein 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 folgenden Komponente Slow wird OnInitializedAsync überschrieben, um eine lang laufende Aufgabe asynchron auszuführen. Während isLoadingtrueist, wird dem Benutzer eine Lademeldung angezeigt. Nachdem das von Task zurückgegebene OnInitializedAsync abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert und die Meldung "Finished!" angezeigt.
Slow.razor:
@page "/slow"
<h2>Slow Component</h2>
@if (isLoading)
{
<div><em>Loading...</em></div>
}
else
{
<div>Finished!</div>
}
@code {
private bool isLoading = true;
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
isLoading = false;
}
private Task LoadDataAsync()
{
return Task.Delay(10000);
}
}
Die vorhergehende Komponente verwendet eine isLoading Variable, um die Lademeldung anzuzeigen. Ein ähnlicher Ansatz wird für eine Komponente verwendet, die Daten in eine Sammlung lädt, und überprüft, ob die Sammlung null ist, um die Lademeldung darzustellen. Im folgenden Beispiel wird die movies-Auflistung auf null überprüft, um entweder die Lademeldung anzuzeigen oder die Auflistung von Filmen anzuzeigen:
@if (movies == null)
{
<p><em>Loading...</em></p>
}
else
{
@* display movies *@
}
@code {
private Movies[]? movies;
protected override async Task OnInitializedAsync()
{
movies = await GetMovies();
}
}
Beim Prerendering wird auf Ruhegewartet, was bedeutet, dass eine Komponente erst dann gerendert wird, wenn alle Komponenten im Renderbaum fertig gerendert sind. Dies bedeutet, dass eine Lademeldung nicht angezeigt wird, während die OnInitializedAsync-Methode einer untergeordneten Komponente während des Prerenderings eine lang andauernde Aufgabe ausführt. Um dieses Verhalten zu veranschaulichen, platzieren Sie die vorhergehende Slow Komponente in die Home Komponente einer Test-App:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<Slow />
Hinweis
Obwohl in den Beispielen in diesem Abschnitt die OnInitializedAsync-Lifecycle-Methode erläutert wird, können andere Lifecycle-Methoden, die während des Vorabrenderings ausgeführt werden, das endgültige Rendering einer Komponente verzögern. Nur OnAfterRender{Async} wird während der Vorrenderung nicht ausgeführt und ist aufgrund der Quieszenz immun gegen Verzögerungen.
Während des Prerendering wird die Home-Komponente erst dann gerendert, wenn die Slow-Komponente gerendert wird, was zehn Sekunden dauert. Die Benutzeroberfläche ist während dieses zehn sekundenigen Zeitraums leer, und es gibt keine Lademeldung. Nach dem Prerendering wird die Home-Komponente gerendert, und die Lademeldung der Slow-Komponente wird angezeigt. Nach zehn weiteren Sekunden zeigt die Slow Komponente schließlich die fertige Meldung an.
Wie die vorstehende Demonstration zeigt, führt die Inaktivität während des Vorrenderns zu einer schlechten Benutzererfahrung. Um die Benutzerfreundlichkeit zu verbessern, sollten Sie zunächst Streaming-Rendering implementieren, damit Sie beim Prerendering nicht auf den Abschluss der asynchronen Aufgabe warten müssen.
Fügen Sie der komponente [StreamRendering] das hinzu (verwenden Sie Slow in .NET 8):
@attribute [StreamRendering]
Wenn die Home Komponente vorab gerendert wird, wird die Slow-Komponente schnell mit der Lademeldung gerendert wird. Die Home Komponente wartet nicht zehn Sekunden, bis die Slow Komponente das Rendern abgeschlossen hat. Die fertige Meldung, die am Ende der Vorabdarstellung angezeigt wird, wird jedoch durch die Lademeldung ersetzt, während die Komponente endgültig gerendert wird, was eine weitere zehnsekündige Verzögerung ist. Dies tritt auf, da die Slow-Komponente zweimal gerendert wird und LoadDataAsync ebenfalls zweimal ausgeführt wird. Wenn eine Komponente auf Ressourcen wie Dienste und Datenbanken zugreift, erzeugt die doppelte Ausführung von Dienstaufrufen und Datenbankabfragen unerwünschte Lasten für die Ressourcen der App.
Um das doppelte Anzeigen der Ladebenachrichtigung und die erneute Ausführung von Dienst- und Datenbankaufrufen zu verhindern, sollten Sie den vorgerenderten Zustand mit PersistentComponentState für das endgültige Rendern der Komponente beibehalten, wie in den folgenden Aktualisierungen der Slow-Komponente zu sehen ist.
@page "/slow"
@attribute [StreamRendering]
<h2>Slow Component</h2>
@if (Data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@Data</div>
}
@code {
[PersistentState]
public string? Data { get; set; }
protected override async Task OnInitializedAsync()
{
Data ??= await LoadDataAsync();
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
}
@page "/slow"
@attribute [StreamRendering]
@implements IDisposable
@inject PersistentComponentState ApplicationState
<h2>Slow Component</h2>
@if (data is null)
{
<div><em>Loading...</em></div>
}
else
{
<div>@data</div>
}
@code {
private string? data;
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<string>(nameof(data), out var restored))
{
data = await LoadDataAsync();
}
else
{
data = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson(nameof(data), data);
return Task.CompletedTask;
}
private async Task<string> LoadDataAsync()
{
await Task.Delay(5000);
return "Finished!";
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
Durch die Kombination von Streamingrendering mit persistentem Komponentenstatus:
- Dienste und Datenbanken erfordern nur einen einzelnen Aufruf für die Komponenteninitialisierung.
- Komponenten rendern ihre Benutzeroberflächen schnell und zeigen während längerer Aufgaben Ladehinweise an, um eine optimale Benutzererfahrung zu gewährleisten.
Weitere Informationen finden Sie in den folgenden Ressourcen:
:::moniker-end
Quieszen während der Vordarstellung führt zu einer schlechten Benutzererfahrung. Die Verzögerung kann in Apps behoben werden, die auf .NET 8 oder höher abzielen, mit einem Feature namens Streamingrendering, das üblicherweise mit der Beibehaltung des Komponentenstatus während des Prerendings kombiniert wird, um zu vermeiden, dass auf den Abschluss der asynchronen Aufgabe gewartet werden muss. In Versionen von .NET vor .NET 8 kann die Ausführung einer lang andauernden Hintergrundaufgabe, die die Daten nach dem endgültigen Rendering lädt, eine lange Renderingverzögerung aufgrund von Inaktivität beheben.
Behandeln von Fehlern
Informationen zum Behandeln von Fehlern während der Ausführung von Lebenszyklusmethoden finden Sie unter Fehler in ASP.NET Core-Apps behandelnBlazor.
Zustandsbehaftete erneute Verbindung nach dem Prerendering
Beim Vorabrendern auf dem Server wird eine 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 Verhalten zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.
Im folgenden Code wird ein WeatherForecastService angezeigt, der die Änderung der Datenanzeige aufgrund des Vorabrenderns vermeidet. Das erwartete Delay-Element (await Task.Delay(...)) simuliert eine kurze Verzögerung, bevor Daten aus der GetForecastAsync-Methode zurückgegeben werden.
Fügen Sie IMemoryCache-Dienste mit AddMemoryCache zur Dienstsammlung in der Program-Datei der App hinzu:
builder.Services.AddMemoryCache();
WeatherForecastService.cs:
using Microsoft.Extensions.Caching.Memory;
namespace BlazorSample;
public class WeatherForecastService(IMemoryCache memoryCache)
{
private static readonly string[] summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
public IMemoryCache MemoryCache { get; } = memoryCache;
public Task<WeatherForecast[]?> GetForecastAsync(DateOnly startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
namespace BlazorSample;
public class WeatherForecastService(IMemoryCache memoryCache)
{
private static readonly string[] summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
public IMemoryCache MemoryCache { get; } = memoryCache;
public Task<WeatherForecast[]?> GetForecastAsync(DateOnly startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]?> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using Microsoft.Extensions.Caching.Memory;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
});
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
var rng = new Random();
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = summaries[rng.Next(summaries.Length)]
}).ToArray();
});
}
}
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;
public class WeatherForecastService
{
private static readonly string[] summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public WeatherForecastService(IMemoryCache memoryCache)
{
MemoryCache = memoryCache;
}
public IMemoryCache MemoryCache { get; }
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return MemoryCache.GetOrCreateAsync(startDate, async e =>
{
e.SetOptions(new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromSeconds(30)
});
var rng = new Random();
await Task.Delay(TimeSpan.FromSeconds(10));
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = summaries[rng.Next(summaries.Length)]
}).ToArray();
});
}
}
Weitere Informationen zu RenderModefinden Sie im Leitfaden zu ASP.NET Core BlazorSignalR.
Der Inhalt dieses Abschnitts konzentriert sich auf Blazor Web Apps und zustandsbehaftete SignalRWiederanschlüsse. Informationen zum Beibehalten des Zustands während der Ausführung des Initialisierungscodes während der Vorrenderung finden Sie unter ASP.NET Core Blazor vorrenderter Zustandsbeständigkeit.
Obwohl sich der Inhalt in diesem Abschnitt auf Blazor Server und die zustandsbehaftete SignalR-Verbindungswiederherstellung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Lösungen (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 der Vorrenderung finden Sie unter Integrieren ASP.NET Core-Komponenten Razor in MVC oder Razor Pages.
Voarabrendering mit JavaScript-Interop
In diesem Abschnitt werden serverseitige Apps beschrieben, die Razor-Komponenten vorab rendern. Das Vorabrendering wird in Vorabrendern von ASP.NET Core-Razor-Komponenten behandelt.
In diesem Abschnitt werden serverseitige Apps beschrieben, die Razor-Komponenten vorab rendern. Das Vorabrendering wird in Vorabrendern von ASP.NET Core-Razor-Komponenten behandelt.
Hinweis
Interne Navigation für interaktives Routing in Blazor Web Apps beinhaltet keine Anforderung neuer Seiteninhalte vom Server. Daher findet das Vorabrendern nicht für interne Seitenanforderungen statt. Wenn die App interaktives Routing verwendet, führen Sie ein vollständiges Neuladen der Seite für Komponentenbeispiele durch, die das Vorabrendern veranschaulichen. Weitere Informationen finden Sie unter ASP.NET Core Blazor Vorrenderter Zustand Persistenz.
In diesem Abschnitt werden serverseitige Anwendungen und gehostete Blazor WebAssembly-Anwendungen beschrieben, die Razor-Komponenten vorab rendern. Prerendering wird in Integrieren von ASP.NET Core-Komponenten Razor mit MVC oder Razor Pages behandelt.
Während der Voreinstellung ist das Aufrufen von JavaScript (JS) nicht möglich. Das folgende Beispiel zeigt, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet wird, die mit dem Vorabrendering kompatibel ist.
Die folgende scrollElementIntoView Funktion:
- Blättert zu dem übergebenen Element mit
scrollIntoView. - Gibt den
top-Eigenschaftswert des Elements von dergetBoundingClientRect-Methode zurück.
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Wenn IJSRuntime.InvokeAsync die JS-Funktion im Komponentencode aufruft, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein HTML-DOM-Element gibt, bis die Komponente gerendert wird.
StateHasChanged (Referenzquelle) wird aufgerufen, um das Rendering der Komponente mit dem neuen Zustand, der durch den JS Interop-Aufruf erhalten wurde, in die Warteschlange zu stellen (weitere Informationen finden Sie unter ASP.NET Core Razor Komponentenrendering). Es wird keine Endlosschleife erstellt, da StateHasChanged nur aufgerufen wird, wenn scrollPosition auf null festgelegt wurde.
PrerenderedInterop.razor:
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<PageTitle>Prerendered Interop</PageTitle>
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
Im vorherigen Beispiel wird der Client mit einer globalen Funktion belastet. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.
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 Server-Neuimplementierung 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 ein CancellationTokenSource und ein CancellationToken.
- Nach der Entsorgung der Komponente und zu jedem Zeitpunkt, an dem ein Abbruch durch manuelles Abbrechen des Tokens gewünscht wird, rufen Sie
CancellationTokenSource.Cancelauf, 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(10000, cts.Token);stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar. -
BackgroundResourceMethodstellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn dieResourcevor dem Aufruf der Methode verworfen wird.
BackgroundWork.razor:
@page "/background-work"
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<PageTitle>Background Work</PageTitle>
<h1>Background Work Example</h1>
<p>
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
</p>
<p>Study logged messages in the console.</p>
<p>
If you trigger disposal within 10 seconds of page load, the
<code>BackgroundResourceMethod</code> isn't executed.
</p>
<p>
If disposal occurs after <code>BackgroundResourceMethod</code> is called but
before action is taken on the resource, an <code>ObjectDisposedException</code>
is thrown by <code>BackgroundResourceMethod</code>, and the resource isn't
processed.
</p>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
private IList<string> messages = [];
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(10000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts?.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose() => disposed = true;
}
}
@page "/background-work"
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<PageTitle>Background Work</PageTitle>
<h1>Background Work Example</h1>
<p>
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
</p>
<p>Study logged messages in the console.</p>
<p>
If you trigger disposal within 10 seconds of page load, the
<code>BackgroundResourceMethod</code> isn't executed.
</p>
<p>
If disposal occurs after <code>BackgroundResourceMethod</code> is called but
before action is taken on the resource, an <code>ObjectDisposedException</code>
is thrown by <code>BackgroundResourceMethod</code>, and the resource isn't
processed.
</p>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
private IList<string> messages = [];
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(10000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
if (!cts.IsCancellationRequested)
{
cts.Cancel();
}
cts?.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose() => disposed = true;
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new();
private CancellationTokenSource cts = new();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger
<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>
@code {
private Resource resource = new Resource();
private CancellationTokenSource cts = new CancellationTokenSource();
protected async Task LongRunningWork()
{
Logger.LogInformation("Long running work started");
await Task.Delay(5000, cts.Token);
cts.Token.ThrowIfCancellationRequested();
resource.BackgroundResourceMethod(Logger);
}
public void Dispose()
{
Logger.LogInformation("Executing Dispose");
cts.Cancel();
cts.Dispose();
resource?.Dispose();
}
private class Resource : IDisposable
{
private bool disposed;
public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
{
logger.LogInformation("BackgroundResourceMethod: Start method");
if (disposed)
{
logger.LogInformation("BackgroundResourceMethod: Disposed");
throw new ObjectDisposedException(nameof(Resource));
}
// Take action on the Resource
logger.LogInformation("BackgroundResourceMethod: Action on Resource");
}
public void Dispose()
{
disposed = true;
}
}
}
Um eine Ladeanzeige anzuzeigen, während die Hintergrundarbeit stattfindet, verwenden Sie den folgenden Ansatz.
Erstellen Sie mit einem Loading-Parameter eine Ladeindikatorkomponente, die untergeordnete Inhalte in einem RenderFragment anzeigen kann. Für den Loading Parameter:
- Wenn
true, zeigen Sie einen Indikator für das Laden an. - Wenn
false, rendern Sie den Inhalt der Komponente (ChildContent). Weitere Informationen finden Sie unter Untergeordnete Inhaltsrenderungsfragmente.
ContentLoading.razor:
@if (Loading)
{
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
}
else
{
@ChildContent
}
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public bool Loading { get; set; }
}
Um CSS-Formatvorlagen für den Indikator zu laden, fügen Sie die Formatvorlagen dem <head> Inhalt mit der HeadContent-Komponente hinzu. Weitere Informationen finden Sie unter Blazor.
@if (Loading)
{
<!-- OPTIONAL ...
<HeadContent>
<style>
...
</style>
</HeadContent>
-->
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
}
else
{
@ChildContent
}
...
Schließen Sie das Markup der Komponente Razor mit der ContentLoading Komponente um, und übergeben Sie einen Wert in einem C#-Feld an den Loading Parameter, wenn die Initialisierung von der Komponente ausgeführt wird:
<ContentLoading Loading="@loading">
...
</ContentLoading>
@code {
private bool loading = true;
...
protected override async Task OnInitializedAsync()
{
await LongRunningWork().ContinueWith(_ => loading = false);
}
...
}
Blazor Server Wiederverbindungsereignisse
Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Ereignishandlern zur Wiederherstellung von serverseitigen Verbindungen ausgeführt. Wenn die SignalR-Verbindung mit dem Client getrennt wird, werden nur die 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.
Zusätzliche Ressourcen
Abgefangene Ausnahmen außerhalb des Lebenszyklus einer Razor Komponente behandeln