ASP.NET Core'da öğe, bileşen ve model ilişkilerini koruma Blazor

Not

Bu, bu makalenin en son sürümü değildir. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

Önemli

Bu bilgiler, ticari olarak piyasaya sürülmeden önce önemli ölçüde değiştirilebilen bir yayın öncesi ürünle ilgilidir. Burada verilen bilgilerle ilgili olarak Microsoft açık veya zımni hiçbir garanti vermez.

Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

Bu makalede, işleme sırasında öğe, bileşen ve model ilişkilerini korumak için yönerge özniteliğinin nasıl kullanılacağı ve öğeler veya bileşenler daha sonra değişirken nasıl kullanılacağı @key açıklanmaktadır.

Yönerge özniteliğinin @key kullanımı

Öğelerin veya bileşenlerin listesini oluştururken ve öğeler ya da bileşenler daha sonra değişirken, Blazor önceki öğelerden veya bileşenlerden hangisinin korunacaklarına ve model nesnelerinin bunlara nasıl eşlenmesi gerektiğine karar vermelidir. Normalde, bu işlem otomatiktir ve genel işleme için yeterlidir, ancak genellikle yönerge özniteliğini kullanarak işlemi denetlemenin @key gerekli olduğu durumlar vardır.

kullanılarak @keyçözülen bir koleksiyon eşleme sorununu gösteren aşağıdaki örneği göz önünde bulundurun.

Aşağıdaki bileşenler için:

  • BileşenDetails, bir <input> öğede görüntülenen üst bileşenden (Data) veri alır. Kullanıcı <input> öğelerinden birini seçtiğinde, görüntülenen belirli <input> öğesi kullanıcıdan sayfanın odağını alabilir.
  • Üst bileşen, bileşeni kullanarak görüntülenecek kişi nesnelerinin Details listesini oluşturur. Her üç saniyede bir koleksiyona yeni bir kişi eklenir.

Bu tanıtım şunları yapmanızı sağlar:

  • İşlenmiş çeşitli Details bileşenleri arasından bir <input> seçin.
  • Kişi koleksiyonu otomatik olarak büyürken sayfanın odağının davranışını inceleyin.

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; }
}

Aşağıdaki üst bileşende, bir kişi OnTimerCallback eklemenin her yinelemesi koleksiyonun tamamının yeniden oluşturulmasına neden olur Blazor . Sayfanın odağı <input> öğelerinin aynı dizin konumunda kaldığından, her kişi eklendiğinde odak kayar. Odağın kullanıcı tarafından seçilen öğenin dışına kaydırılması istenen bir davranış değildir. Aşağıdaki bileşenin hatalı davranışı gösterildikten sonra, kullanıcının deneyimini geliştirmek için @key yönerge özniteliği kullanılmıştır.

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; }
    }
}

people koleksiyonun içeriği eklenen, silinen veya yeniden sıralanan girdilerle değişir. Yeniden işleme, görünür davranış farklılıklarına yol açabilir. Örneğin, bir kişi koleksiyona people her eklendiğinde kullanıcının odağı kaybolur.

Öğeleri veya bileşenleri koleksiyona eşleme işlemi @key yönerge özniteliği ile denetlenebilir. @key yönerge özniteliğinin kullanılması, anahtarın değerine dayanarak öğelerin veya bileşenlerin korunmasını garanti eder. Yukarıdaki örnekte yer alan Details bileşeninde person öğesine anahtar uygulanırsa, Blazor değişmemiş olan Details bileşenlerinin yeniden işlenmesini yoksayar.

Üst bileşeni, yönerge özniteliğini @key koleksiyonla people kullanacak şekilde değiştirmek için öğesini aşağıdaki şekilde güncelleştirin <Details> :

<Details @key="person" Data="@person.Data" />

people koleksiyonu değiştiğinde Details örnekleri ile person örnekleri arasındaki ilişki korunur. Koleksiyonun başlangıcına bir Person eklendiğinde, buna karşılık gelen konuma yeni bir Details örneği eklenir. Diğer örnekler olduğu gibi bırakılır. Bu nedenle koleksiyona kişiler eklendikçe kullanıcının odağı kaybolmaz.

@key yönerge özniteliği kullanıldığında diğer koleksiyon güncelleştirmeleri de aynı davranışı gösterir:

  • Koleksiyondan örnek silindiğinde kullanıcı arabiriminden yalnızca silinen örneğe karşılık gelen bileşen örneği kaldırılır. Diğer örnekler olduğu gibi bırakılır.
  • Koleksiyon girdileri yeniden sıralanır, kullanıcı arabiriminde bunlara karşılık gelen bileşen örnekleri korunur ve yeniden sıralanır.

Önemli

Anahtarlar her kapsayıcı öğesi veya bileşeni için yereldir. Anahtarlar belgede genel olarak karşılaştırılamaz.

@key ne zaman kullanılmalı?

Normalde her liste işlendiğinde (örneğin bir foreach bloğunda) ve @key yönerge özniteliğini tanımlayacak uygun bir değer olduğunda @key kullanmak mantıklıdır.

Ayrıca aşağıdaki örneklerde gösterildiği gibi, bir nesne değişmediğinde öğe veya bileşen alt ağacını korumak için de @key kullanabilirsiniz.

Örnek 1:

<li @key="person">
    <input value="@person.Data" />
</li>

Örnek 2:

<div @key="person">
    @* other HTML elements *@
</div>

Bir person örneği değiştiğinde @key öznitelik yönergesi Blazor'ı şunları yapmaya zorlar:

  • <li> ve <div> öğelerinin tamamını ve bunların alt öğelerini atma.
  • Kullanıcı arabirimi içinde yeni öğeler ve bileşenlerle alt ağacı yeniden oluşturma.

Alt ağaç içinde koleksiyon değiştiğinde hiçbir kullanıcı arabirimi durumunun korunmadığından emin olmak için bu yararlıdır.

@key kapsamı

@key öznitelik yönergesinin kapsamı, üst öğesi içindeki kendi eşdüzeyleri olarak belirlenir.

Aşağıdaki örneği inceleyin. first ile second anahtarları, dış <div> öğesiyle aynı kapsam içinde birbiriyle karşılaştırılır:

<div>
    <div @key="first">...</div>
    <div @key="second">...</div>
</div>

Aşağıdaki örnek first ile second anahtarlarını kendi kapsamlarında, birbirleriyle ilişkisiz olarak ve birbirlerini etkilemeyecek şekilde gösterir. Her @key kapsamı yalnızca kendi üst <div> öğesine uygulanır; üst <div> öğeleri arasında uygulanmaz:

<div>
    <div @key="first">...</div>
</div>
<div>
    <div @key="second">...</div>
</div>

Daha önce gösterilen Details bileşeni için, aşağıdaki örnekler aynı @key kapsamı içinde person verilerini işler ve @key için tipik kullanım örneklerini gösterir:

<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>

Aşağıdaki örneklerde @key kapsamı yalnızca her Details bileşen örneğini çevreleyen <div> veya <li> öğesidir. Bu nedenle people koleksiyonunun her üyesinin person verilerine, işlenen Details bileşenleri genelindeki her person örneğinde anahtar uygulanmaz. @key kullanırken aşağıdaki desenlerden kaçının:

@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>

@key ne zaman kullanılmamalı?

@key ile işlemenin performans maliyeti vardır. Performans maliyeti yüksek değildir ama @key yönerge özniteliğini yalnızca öğeyi veya bileşeni korumanın uygulama açısından faydalı olacağı durumlarda belirtin.

@key kullanılmasa bile Blazor alt öğe ve bileşen örneklerini mümkün olduğunca korur. @key kullanmanın tek avantajı, model örneklerinin korunan bileşen örneklerine nasıl eşleneceğini Blazor'ın seçmesi yerine bu eşleme üzerinde denetim sahibi olmaktır.

@key için kullanılacak değerler

Genellikle @key için aşağıdaki değerlerden birini sağlamak mantıklı olur:

  • Model nesnesi örnekleri. Örneğin yukarıdaki örnekte Person örneği (person) kullanılmıştı. Bu, nesne başvurusu eşitliği temelinde koruma sağlar.
  • Benzersiz tanımlayıcılar. Örneğin benzersiz tanımlayıcılar int, string veya Guid türündeki birincil anahtar değerlerini temel alabilir.

@key için kullanılan değerlerin çakışmadığından emin olun. Aynı üst öğede çakışan değerler algılanırsa, Blazor eski öğeleri veya bileşenleri belirleyici bir şekilde yeni öğelere veya bileşenlere eşleyemediğinden bir özel durum oluşturur. Yalnızca nesne örnekleri veya birincil anahtar değerleri gibi farklı değerler kullanın.