zaawansowane scenariusze ASP.NET Core Blazor (tworzenie drzewa renderowania)
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
W tym artykule opisano zaawansowany scenariusz ręcznego tworzenia Blazor drzew renderowanych za pomocą polecenia RenderTreeBuilder.
Ostrzeżenie
Tworzenie składników RenderTreeBuilder jest zaawansowanym scenariuszem. Źle sformułowany składnik (na przykład nieujawiony tag znaczników) może spowodować niezdefiniowane zachowanie. Niezdefiniowane zachowanie obejmuje uszkodzone renderowanie zawartości, utratę funkcji aplikacji i zabezpieczenia naruszone.
Ręczne tworzenie drzewa renderowania (RenderTreeBuilder
)
RenderTreeBuilder Udostępnia metody manipulowania składnikami i elementami, w tym ręczne kompilowanie składników w kodzie języka C#.
Rozważmy następujący PetDetails
składnik, który można ręcznie renderować w innym składniku.
PetDetails.razor
:
<h2>Pet Details</h2>
<p>@PetDetailsQuote</p>
@code
{
[Parameter]
public string? PetDetailsQuote { get; set; }
}
W poniższym BuiltContent
składniku pętla w metodzie CreateComponent
generuje trzy PetDetails
składniki.
W RenderTreeBuilder metodach z numerem sekwencji numery sekwencji to numery wierszy kodu źródłowego. Algorytm Blazor różnicy opiera się na numerach sekwencji odpowiadających odrębnym wierszom kodu, a nie odrębnym wywołaniom wywołań. Podczas tworzenia składnika za pomocą RenderTreeBuilder metod zakoduj argumenty dla numerów sekwencji. Użycie obliczenia lub licznika do wygenerowania numeru sekwencji może prowadzić do niskiej wydajności. Aby uzyskać więcej informacji, zobacz Sekcję Numery sekwencji odnoszą się do numerów wierszy kodu, a nie z kolejnością wykonywania.
BuiltContent.razor
:
@page "/built-content"
<PageTitle>Built Content</PageTitle>
<h1>Built Content Example</h1>
<div>
@CustomRender
</div>
<button @onclick="RenderComponent">
Create three Pet Details components
</button>
@code {
private RenderFragment? CustomRender { get; set; }
private RenderFragment CreateComponent() => builder =>
{
for (var i = 0; i < 3; i++)
{
builder.OpenComponent(0, typeof(PetDetails));
builder.AddAttribute(1, "PetDetailsQuote", "Someone's best friend!");
builder.CloseComponent();
}
};
private void RenderComponent() => CustomRender = CreateComponent();
}
@page "/built-content"
<PageTitle>Built Content</PageTitle>
<h1>Built Content Example</h1>
<div>
@CustomRender
</div>
<button @onclick="RenderComponent">
Create three Pet Details components
</button>
@code {
private RenderFragment? CustomRender { get; set; }
private RenderFragment CreateComponent() => builder =>
{
for (var i = 0; i < 3; i++)
{
builder.OpenComponent(0, typeof(PetDetails));
builder.AddAttribute(1, "PetDetailsQuote", "Someone's best friend!");
builder.CloseComponent();
}
};
private void RenderComponent() => CustomRender = CreateComponent();
}
@page "/built-content"
<h1>Build a component</h1>
<div>
@CustomRender
</div>
<button @onclick="RenderComponent">
Create three Pet Details components
</button>
@code {
private RenderFragment? CustomRender { get; set; }
private RenderFragment CreateComponent() => builder =>
{
for (var i = 0; i < 3; i++)
{
builder.OpenComponent(0, typeof(PetDetails));
builder.AddAttribute(1, "PetDetailsQuote", "Someone's best friend!");
builder.CloseComponent();
}
};
private void RenderComponent()
{
CustomRender = CreateComponent();
}
}
Ostrzeżenie
Typy w Microsoft.AspNetCore.Components.RenderTree programie umożliwiają przetwarzanie wyników operacji renderowania. Są to wewnętrzne szczegóły implementacji Blazor platformy. Te typy powinny być uważane za niestabilne i mogą ulec zmianie w przyszłych wersjach.
Numery sekwencji odnoszą się do numerów wierszy kodu, a nie kolejności wykonywania
Razor pliki składników (.razor
) są zawsze kompilowane. Wykonywanie skompilowanego kodu ma potencjalną przewagę nad interpretowaniem kodu, ponieważ krok kompilacji, który daje skompilowany kod, może służyć do wstrzykiwania informacji, które zwiększają wydajność aplikacji w czasie wykonywania.
Kluczowym przykładem tych ulepszeń są numery sekwencji. Numery sekwencji wskazują środowisko uruchomieniowe, z którego pochodzą dane wyjściowe, z których pochodzą odrębne i uporządkowane wiersze kodu. Środowisko uruchomieniowe używa tych informacji do generowania wydajnych różnic drzewa w czasie liniowym, co jest znacznie szybsze niż zwykle w przypadku ogólnego algorytmu różnic drzewa.
Rozważmy następujący Razor plik składnika (.razor
):
@if (someFlag)
{
<text>First</text>
}
Second
Razor Powyższe znaczniki i zawartość tekstowa są kompilowane w kodzie języka C# podobnym do następującego:
if (someFlag)
{
builder.AddContent(0, "First");
}
builder.AddContent(1, "Second");
Gdy kod jest wykonywany po raz pierwszy i someFlag
ma true
wartość , konstruktor otrzymuje sekwencję w poniższej tabeli.
Sequence | Typ | Data |
---|---|---|
0 | Węzeł tekstowy | First |
1 | Węzeł tekstowy | Second |
Wyobraź sobie, że staje false
się i someFlag
adiustacja jest renderowana ponownie. Tym razem konstruktor otrzymuje sekwencję w poniższej tabeli.
Sequence | Typ | Data |
---|---|---|
1 | Węzeł tekstowy | Second |
Gdy środowisko uruchomieniowe wykonuje różnicę, zobaczy, że element w sekwencji 0
został usunięty, dlatego generuje następujący trywialny skrypt edycji z jednym krokiem:
- Usuń pierwszy węzeł tekstowy.
Problem z programowym generowaniem numerów sekwencji
Załóżmy, że zamiast tego napisaliśmy następującą logikę konstruktora drzewa renderowania:
var seq = 0;
if (someFlag)
{
builder.AddContent(seq++, "First");
}
builder.AddContent(seq++, "Second");
Pierwsze dane wyjściowe zostaną odzwierciedlone w poniższej tabeli.
Sequence | Typ | Data |
---|---|---|
0 | Węzeł tekstowy | First |
1 | Węzeł tekstowy | Second |
Ten wynik jest identyczny z poprzednim przypadkiem, więc nie istnieją żadne negatywne problemy. someFlag
jest false
w drugim renderowaniu, a dane wyjściowe są widoczne w poniższej tabeli.
Sequence | Typ | Data |
---|---|---|
0 | Węzeł tekstowy | Second |
Tym razem algorytm różnicy widzi, że wystąpiły dwie zmiany. Algorytm generuje następujący skrypt edycji:
- Zmień wartość pierwszego węzła tekstowego na
Second
. - Usuń drugi węzeł tekstowy.
Generowanie numerów sekwencji straciło wszystkie przydatne informacje o tym, gdzie if/else
gałęzie i pętle były obecne w oryginalnym kodzie. Powoduje to różnice dwa razy więcej niż wcześniej.
Jest to banalny przykład. W bardziej realistycznych przypadkach ze złożonymi i głęboko zagnieżdżonym strukturami, a zwłaszcza w przypadku pętli, koszt wydajności jest zwykle wyższy. Zamiast natychmiast identyfikować bloki pętli lub gałęzie, które zostały wstawione lub usunięte, algorytm różnicy musi się powtarzać głęboko w drzewach renderowania. Zwykle skutkuje to tworzeniem dłuższych skryptów edycji, ponieważ algorytm różnicy jest błędnie poinformowany o tym, jak stare i nowe struktury odnoszą się do siebie nawzajem.
Wskazówki i wnioski
- Wydajność aplikacji występuje w przypadku dynamicznego generowania numerów sekwencji.
- Niezbędne informacje nie istnieją, aby umożliwić platformie automatyczne generowanie numerów sekwencji w czasie wykonywania, chyba że informacje są przechwytywane w czasie kompilacji.
- Nie zapisuj długich bloków logiki implementowanych RenderTreeBuilder ręcznie. Preferuj
.razor
pliki i zezwól kompilatorowi na radzenie sobie z numerami sekwencji. Jeśli nie możesz uniknąć ręcznej RenderTreeBuilder logiki, podziel długie bloki kodu na mniejsze elementy opakowane w OpenRegion/CloseRegion wywołania. Każdy region ma własną oddzielną przestrzeń numerów sekwencji, więc można uruchomić ponownie z zera (lub dowolnej innej liczby) wewnątrz każdego regionu. - Jeśli numery sekwencji są zakodowane na stałe, algorytm różnicy wymaga tylko zwiększenia wartości liczb sekwencji. Początkowa wartość i luki są nieistotne. Jedną z uzasadnionych opcji jest użycie numeru wiersza kodu jako numeru sekwencji lub rozpoczęcie od zera i zwiększenie o te lub setki (lub dowolny preferowany interwał).
- W przypadku pętli numery sekwencji powinny zwiększać się w kodzie źródłowym, a nie pod względem zachowania środowiska uruchomieniowego. Fakt, że w czasie wykonywania liczby powtarzają się, to sposób, w jaki system różnicowania zdaje sobie sprawę, że jesteś w pętli.
- Blazor używa numerów sekwencji, podczas gdy inne struktury interfejsu użytkownika dzielące drzewa nie używają ich. Różnicowanie jest znacznie szybsze, gdy są używane numery sekwencji i Blazor ma zaletę kroku kompilacji, który automatycznie zajmuje się numerami sekwencji dla deweloperów tworzących
.razor
pliki.