Not
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i .NET 10-versionen av den här artikeln.
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .
Den här artikeln beskriver hur du använder direktivattributet @key för att bibehålla relationerna mellan element, komponenter och modeller vid återgivning, när dessa därefter förändras.
Användning av direktivattributet @key
När du återger en lista över element eller komponenter och elementen eller komponenterna därefter ändras, Blazor måste du bestämma vilka av de tidigare elementen eller komponenterna som ska behållas och hur modellobjekt ska mappas till dem. Normalt sett är den här processen automatisk och tillräcklig för allmän återgivning, men det finns ofta fall där kontroll av processen med hjälp av direktivattributet @key krävs.
Tänk på följande exempel som visar ett problem med samlingsmappning som löses med hjälp @keyav .
För följande komponenter:
- Komponenten
Detailstar emot data (Data) från den överordnade komponenten, som visas i ett<input>element. Varje visat<input>-element kan få sidans fokus från användaren när de väljer ett av<input>-elementen. - Den överordnade komponenten skapar en lista över personobjekt som ska visas med hjälp av komponenten
Details. Var tredje sekund läggs en ny person till i samlingen.
Med den här demonstrationen kan du:
- Välj en
<input>bland flera renderadeDetailskomponenter. - Studera beteendet för sidans fokus när personsamlingen växer automatiskt.
Details.razor:
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string Data { get; set; }
}
<input value="@Data" />
@code {
[Parameter]
public string Data { get; set; }
}
I följande överordnade komponent resulterar varje iteration av att lägga till en person i OnTimerCallback i att hela samlingen återskapas i Blazor. Sidans fokus ligger kvar på samma indexposition<input> för element, så fokus flyttas varje gång en person läggs till. Det är inte önskvärt att flytta fokus från det som användaren valde. När du har demonstrerat det dåliga beteendet med följande komponent används direktivattributet @key för att förbättra användarens upplevelse.
People.razor:
@page "/people"
@using System.Timers
@implements IDisposable
<PageTitle>People</PageTitle>
<h1>People Example</h1>
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
People.razor:
@page "/people"
@using System.Timers
@implements IDisposable
<PageTitle>People</PageTitle>
<h1>People Example</h1>
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
PeopleExample.razor:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
PeopleExample.razor:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
PeopleExample.razor:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string Data { get; set; }
}
}
PeopleExample.razor:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new List<Person>()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string Data { get; set; }
}
}
Innehållet i people samlingen ändras med infogade, borttagna eller ombeställda poster. Rerendering kan leda till synliga beteendeskillnader. Varje gång en person till exempel infogas i people samlingen går användarens fokus förlorat.
Mappningsprocessen för element eller komponenter till en samling kan styras med direktivattributet @key . Användning av @key garanterar bevarande av element eller komponenter baserat på nyckelns värde. Om komponenten Details i föregående exempel är nycklad på person-objektet, ignorerar Blazor att återrendera Details-komponenter som inte har ändrats.
För att ändra den överordnade komponenten och använda direktivattributet @key med samlingen people, uppdatera elementet <Details> till följande:
<Details @key="person" Data="@person.Data" />
När samlingen people ändras behålls associationen mellan Details instanser och person instanser. När en Person infogas i början av samlingen infogas en ny Details instans vid motsvarande position. Andra instanser lämnas oförändrade. Därför går inte användarens fokus förlorat eftersom personer läggs till i samlingen.
Andra samlingsuppdateringar har samma beteende när direktivattributet @key används:
- Om en instans tas bort från samlingen tas endast motsvarande komponentinstans bort från användargränssnittet. Andra instanser lämnas oförändrade.
- Om samlingsposter sorteras om bevaras motsvarande komponentinstanser och sorteras om i användargränssnittet.
Viktigt!
Nycklarna är lokala för varje containerelement eller komponent. Nycklar jämförs inte globalt i hela dokumentet.
När du ska använda @key
Vanligtvis är det klokt att använda @key när en lista återges (till exempel i ett foreach block) och det finns ett lämpligt värde för att definiera @key.
Du kan också använda @key för att bevara ett element eller komponentunderträd när ett objekt inte ändras, som följande exempel visar.
Exempel 1:
<li @key="person">
<input value="@person.Data" />
</li>
Exempel 2:
<div @key="person">
@* other HTML elements *@
</div>
Om en person-instans ändras, tvingar attributdirektivet @keyBlazor att:
- Ta bort hela
<li>eller<div>och deras underordnade. - Återskapa underträdet i användargränssnittet med nya element och komponenter.
Detta är användbart för att garantera att inget användargränssnittstillstånd bevaras när samlingen ändras i ett underträd.
Omfång för @key
Attributdirektivet @key är begränsat till sina syskon inom sin förälder.
Tänk dig följande exempel. Nycklarna first och second jämförs mot varandra inom samma omfång för det yttre <div> elementet:
<div>
<div @key="first">...</div>
<div @key="second">...</div>
</div>
I följande exempel visas first och second nycklar i deras egna omfång, som inte är relaterade till varandra och utan påverkan på varandra. Varje @key omfång gäller endast för dess överordnade <div> element, inte över de överordnade <div> elementen:
<div>
<div @key="first">...</div>
</div>
<div>
<div @key="second">...</div>
</div>
För komponenten Details som visades tidigare renderar person följande exempel data inom samma @key omfång och visar typiska användningsfall för @key:
<div>
@foreach (var person in people)
{
<Details @key="person" Data="@person.Data" />
}
</div>
@foreach (var person in people)
{
<div @key="person">
<Details Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li @key="person">
<Details Data="@person.Data" />
</li>
}
</ol>
Följande exempel omfattar endast @key-elementet till <div> eller <li> element som omger varje Details komponentinstans.
person Därför är data för varje medlem i people samlingen inte nyckelade på varje person instans över de renderade Details komponenterna. Undvik följande mönster när du använder @key:
@foreach (var person in people)
{
<div>
<Details @key="person" Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li>
<Details @key="person" Data="@person.Data" />
</li>
}
</ol>
När du inte ska använda @key
Det finns en prestandakostnad vid rendering med @key. Prestandakostnaden är inte stor, men ange @key bara om bevarandet av elementet eller komponenten gynnar appen.
Även om @key inte används, bevarar Blazor underordnade element- och komponentinstanser så mycket som möjligt. Den enda fördelen med att använda @key är kontroll över hur modellinstanser mappas till de bevarade komponentinstanserna i stället för Blazor att välja mappningen.
Värden som ska användas för @key
I allmänhet är det klokt att ange något av följande värden för @key:
- Modellobjektinstanser. Till exempel användes instansen
Person(person) i det tidigare exemplet. Detta säkerställer bevarande baserat på objektreferensjämlikhet. - Unika identifierare. Till exempel kan unika identifierare baseras på primära nyckelvärden av typen
int,stringellerGuid.
Se till att värden som används för @key inte kolliderar. Om motstridiga värden identifieras i samma överordnade element utlöser Blazor ett undantag eftersom det inte deterministiskt kan mappa gamla element eller komponenter till nya element eller komponenter. Använd endast distinkta värden, till exempel objektinstanser eller primärnyckelvärden.
ASP.NET Core