Megosztás a következőn keresztül:


Elem-, összetevő- és modellkapcsolatok megőrzése a ASP.NET Core-ban Blazor

Megjegyzés:

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Figyelmeztetés

A ASP.NET Core ezen verziója már nem támogatott. További információt a .NET és a .NET Core támogatási szabályzatában talál. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .

Ez a cikk bemutatja, hogyan őrizheti meg az elem-, összetevő- és modellkapcsolatokat az @key irányelvattribútum használatával a renderelés során, majd az elemek vagy összetevők későbbi módosításakor.

Az @key irányelv attribútum használata

Az elemek vagy összetevők listájának renderelésekor, valamint az elemek vagy összetevők későbbi módosításakor el kell döntenie, Blazor hogy az előző elemek vagy összetevők közül melyik maradjon meg, és hogy a modellobjektumok hogyan legyenek megfeleltetve hozzájuk. Ez a folyamat általában automatikus és elegendő az általános rendereléshez, de gyakran előfordul, hogy az irányelv attribútum használatával kell szabályozni a @key folyamatot.

Tekintse meg az alábbi példát, amely egy gyűjteményleképezési problémát mutat be, amelyet a használatával @keyoldottunk meg.

A következő összetevők esetén:

  • Az Details összetevő adatokat (Data) fogad a szülőösszetevőtől, amely egy <input> elemben jelenik meg. Bármely megjelenített <input> elem megkaphatja az oldal fókuszát a felhasználótól, amikor kiválasztja az <input> egyik elemet.
  • A szülőösszetevő létrehozza a személyobjektumok listáját az Details összetevő használatával való megjelenítéshez. Három másodpercenként egy új személy kerül a gyűjteménybe.

Ez a bemutató a következőt teszi lehetővé:

  • Válasszon egy <input> komponenst a több renderelt Details közül.
  • Tanulmányozza a lap fókuszának viselkedését, ahogy a személyek gyűjteménye automatikusan növekszik.

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

A következő szülőösszetevőben egy személy OnTimerCallback hozzáadásának minden iterációja Blazor a teljes gyűjtemény újraépítését eredményezi. A lap fókusza az elemek indexpozícióján<input> marad, így a fókusz minden egyes személy hozzáadásakor eltolódik. A fókusz eltolása attól, amit a felhasználó kiválasztott, nem kívánatos viselkedés. Miután a következő összetevővel szemlélteti a gyenge viselkedést, az @key irányelv attribútum a felhasználói élmény javítására szolgál.

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

A gyűjtemény tartalma people a beszúrt, törölt vagy újrarendezett bejegyzésekkel változik. Az újrarendezés látható viselkedésbeli különbségekhez vezethet. Például minden alkalommal, amikor egy személyt beszúr a people gyűjteménybe, a felhasználó fókusza elveszik.

A gyűjtemény elemeinek vagy összetevőinek leképezési folyamata az @key irányelv attribútummal vezérelhető. @key A használat garantálja az elemek vagy összetevők megőrzését a kulcs értéke alapján. Ha az összetevő Details az előző példában az elemhez person van kapcsolva, akkor Blazor figyelmen kívül hagyja az azon összetevők újrarajzolását Details, amelyek nem változtak.

A szülőösszetevő módosításához, hogy az @key irányelvet használja a people gyűjteményhez, frissítse a <Details> elemet a következőre:

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

Amikor a people gyűjtemény megváltozik, a Details példányok és a person példányok közötti társítás megmarad. Amikor a gyűjtemény elejére egy új Person példányt szúr be, a rendszer egy új Details példányt helyez el a megfelelő pozícióba. A többi példány változatlan marad. Ezért a felhasználó fókusza nem vesz el, mivel a rendszer felveszi a felhasználókat a gyűjteménybe.

Más gyűjteményfrissítések is ugyanazt a viselkedést mutatják, amikor az @key irányelv attribútumot használják:

  • Ha egy példányt töröl a gyűjteményből, a rendszer csak a megfelelő összetevőpéldányt távolítja el a felhasználói felületről. A többi példány változatlan marad.
  • Ha a gyűjteménybejegyzések újra vannak rendezve, a megfelelő összetevőpéldányok megmaradnak, és újrarendezhetők a felhasználói felületen.

Fontos

A kulcsok minden tárolóelemhez vagy összetevőhöz helyiek. A kulcsok globálisan nincsenek összehasonlítva a dokumentumban.

Mikor érdemes használni a @key

Általában érdemes a listák megjelenítésekor (például blokkokban@key) használniforeach, és megfelelő érték létezik a @keydefinícióhoz.

Egy elem vagy összetevő részhalmaza akkor is @key megőrizhető, ha egy objektum nem változik, ahogy az alábbi példák is mutatják.

1. példa:

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

2. példa:

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

Ha egy person példány megváltozik, az attribútum irányelve a @key következőre kényszeríti Blazor :

  • Az egész <li> és <div> elemeket és azok leszármazottait el kell vetni.
  • Új elemekkel és összetevőkkel építse újra a részhalmazt a felhasználói felületen.

Ez hasznos annak garantálásához, hogy a gyűjtemény egy részhalmazon belüli módosításakor ne legyen megőrzve a felhasználói felület állapota.

Hatóköre @key

Az @key attribútumirányelv a szülőn belüli testvéreire vonatkozik.

Vegye figyelembe az alábbi példát. first és second kulcsokat egymással hasonlítják össze a külső <div> elem azonos hatókörén belül.

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

Az alábbi példa a saját hatókörükben szemlélteti first a second kulcsokat, amelyek nem kapcsolódnak egymáshoz, és nem befolyásolják egymást. Az egyes @key hatókörök csak a szülőelemre <div> vonatkoznak, a szülőelemekre <div> nem:

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

Details A korábban bemutatott összetevő esetében az alábbi példák ugyanazon person a hatókörön belül jelenítik meg @key az adatokat, és bemutatják a következő általános használati eseteket@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>

Az alábbi példák csak az @key elem <div> vagy <li> elemére terjesztik ki a hatókört, amelyek az egyes Details összetevőpéldányokat veszik körül. Ezért a person gyűjtemény minden tagjára vonatkozó adatok people kulcsozódnak a renderelt összetevők minden person példányán. Kerülje a következő mintákat a használat során @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>

Mikor ne használja? @key

A renderelésnek teljesítménybeli költsége van, ha @key-t használ. A teljesítményköltség nem nagy, de csak azt határozza meg @key , hogy az elem vagy összetevő megőrzése előnyös-e az alkalmazás számára.

Még ha @key nincs is használatban, Blazor a lehető legnagyobb mértékben megőrzi a gyermekelemeket és az összetevők példányait. A @key használatának egyetlen előnye az, hogy az iránytást gyakorolhat azzal kapcsolatban, Blazor legyenek a modellpéldányok leképezve a megőrzött összetevőpéldányokra, ahelyett, hogy a leképezést választaná ki.

A következőhöz használandó értékek: @key

Általában érdemes a következő értékek @keyegyikét megadni:

  • Modellobjektum-példányok. A korábbi példában például a Person példányt (person) használták. Ez az objektumhivatkozások egyenlőségén alapuló megőrzést biztosítja.
  • Egyedi azonosítók. Az egyedi azonosítók például olyan elsődleges kulcs értékeken alapulhatnak, amelyek a int, string, vagy Guid típushoz tartoznak.

Győződjön meg arról, hogy a @key használt értékek nem ütköznek. Ha az összeütközési értékeket ugyanabban a szülőelemben észleli, kivételt jelez, Blazor mert a régi elemeket vagy összetevőket nem tudja determinisztikus módon leképezni az új elemekre vagy összetevőkre. Csak különböző értékeket használjon, például objektumpéldányokat vagy elsődleges kulcsértékeket.