Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.
Este artigo descreve o cenário avançado para a construção manual de árvores de renderização com Blazor.
Advertência
O uso de RenderTreeBuilder para criar componentes é um cenário avançado. Um componente deformado (por exemplo, uma etiqueta de marcação não fechada) pode resultar em comportamentos indefinidos. Comportamentos indefinidos incluem renderização de conteúdo danificada, perda de funcionalidades da aplicação e segurança comprometida.
Constrói manualmente uma árvore de renderização (RenderTreeBuilder)
RenderTreeBuilder fornece métodos para manipular componentes e elementos, incluindo a construção manual de componentes em código C#.
Considere o componente seguinte PetDetails , que pode ser renderizado manualmente noutro componente.
PetDetails.razor:
<h2>Pet Details</h2>
<p>@PetDetailsQuote</p>
@code
{
[Parameter]
public string? PetDetailsQuote { get; set; }
}
No componente seguinte BuiltContent , o laço do CreateComponent método gera três PetDetails componentes.
Em métodos RenderTreeBuilder que utilizam números de sequência, estes equivalem aos números de linha no código fonte. O Blazor algoritmo de diferença baseia-se nos números de sequência correspondentes a linhas de código distintas, não em invocações de chamadas distintas. Ao criar um componente com RenderTreeBuilder métodos, codifica os argumentos para números de sequência. Usar um cálculo ou contador para gerar o número de sequência pode levar a um desempenho fraco. Para mais informações, consulte a secção Números de sequência relacionados com números de linha de código e não com a ordem de execução.
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();
}
}
Advertência
Os tipos em Microsoft.AspNetCore.Components.RenderTree permitem o processamento dos resultados das operações de renderização. Estes são detalhes internos da implementação do Blazor framework. Estes tipos devem ser considerados instáveis e sujeitos a alterações em futuras versões.
Os números de sequência referem-se aos números de linha de código e não à ordem de execução
Razor ficheiros de componentes (.razor) são sempre compilados. Executar código compilado tem uma vantagem potencial em relação à interpretação do código porque a etapa de compilação que produz o código compilado pode ser usada para injetar informação que melhora o desempenho da aplicação em tempo de execução.
Um exemplo chave destas melhorias envolve os números de sequência. Os números de sequência indicam ao tempo de execução do programa quais saídas vieram de quais linhas de código distintas e ordenadas. O tempo de execução utiliza estas informações para produzir diferenças de árvore eficientes em tempo linear, o que é muito mais rápido do que normalmente é possível para um algoritmo geral de diferenciação de árvore.
Considere o seguinte Razor ficheiro de componentes (.razor):
@if (someFlag)
{
<text>First</text>
}
Second
A marcação Razor anterior e o conteúdo de texto compilam-se em código C# de forma semelhante ao seguinte:
if (someFlag)
{
builder.AddContent(0, "First");
}
builder.AddContent(1, "Second");
Quando o código é executado pela primeira vez e someFlag é true, o construtor recebe a sequência na tabela seguinte.
| Sequence | Tipo | Data |
|---|---|---|
| 0 | Nó de texto | First |
| 1 | Nó de texto | Second |
Imagine que someFlag se torna false e a marcação é renderizada novamente. Desta vez, o construtor recebe a sequência na tabela seguinte.
| Sequence | Tipo | Data |
|---|---|---|
| 1 | Nó de texto | Second |
Quando o runtime executa um diff, vê que o item na sequência 0 foi removido, por isso gera o seguinte script de edição trivial com um único passo:
- Remove o primeiro nó de texto.
O problema de gerar números de sequência programaticamente
Imagina, em vez disso, que escreveste a seguinte lógica para o render tree builder:
var seq = 0;
if (someFlag)
{
builder.AddContent(seq++, "First");
}
builder.AddContent(seq++, "Second");
A primeira saída está refletida na tabela seguinte.
| Sequence | Tipo | Data |
|---|---|---|
| 0 | Nó de texto | First |
| 1 | Nó de texto | Second |
Este resultado é idêntico ao caso anterior, pelo que não existem problemas negativos.
someFlag é false na segunda renderização, e a saída pode ser vista na tabela seguinte.
| Sequence | Tipo | Data |
|---|---|---|
| 0 | Nó de texto | Second |
Desta vez, o algoritmo de diferença verifica que ocorreram duas alterações. O algoritmo gera o seguinte script de edição:
- Mude o valor do primeiro nó de texto para
Second. - Remova o segundo nó de texto.
A geração dos números de sequência perdeu toda a informação útil sobre a localização dos ramos e ciclos no código original. Isto resulta numa diferença duas vezes mais longa do que antes.
Este é um exemplo trivial. Em casos mais realistas com estruturas complexas e profundamente aninhadas, e especialmente com loops, o custo de performance é geralmente maior. Em vez de identificar imediatamente quais blocos de loop ou ramos foram inseridos ou removidos, o algoritmo diff deve realizar uma recursão profunda nas árvores de renderização. Isto normalmente resulta na construção de scripts de edição mais longos porque o algoritmo de diferença está mal informado sobre como as estruturas antiga e nova se relacionam entre si.
Orientações e conclusões
- O desempenho da aplicação sofre se os números de sequência forem gerados dinamicamente.
- A informação necessária não existe para permitir que o framework gere números de sequência automaticamente em tempo de execução, a menos que a informação seja capturada em tempo de compilação.
- Não escrevas blocos longos de lógica implementada RenderTreeBuilder manualmente. Prefira
.razorficheiros e permita que o compilador trate dos números de sequência. Se não conseguires evitar a lógica manual RenderTreeBuilder , divide blocos longos de código em pedaços mais pequenos encapsulados em OpenRegion/CloseRegion chamadas. Cada região tem o seu próprio espaço separado de números de sequência, por isso podes recomeçar do zero (ou qualquer outro número arbitrário) dentro de cada região. - Se os números de sequência forem codificados fixamente, o algoritmo de diferença apenas exige que os números de sequência aumentem de valor. O valor inicial e as lacunas são irrelevantes. Uma opção legítima é usar o número da linha de código como número de sequência, ou começar de zero e aumentar em uns ou centenas (ou qualquer intervalo preferido).
- Para os loops, os números de sequência devem aumentar no seu código-fonte, e não em relação ao comportamento durante a execução. O facto de, em tempo de execução, os números se repetirem é a forma como o sistema de diferenciação percebe que estás num ciclo.
-
Blazor usa números de sequência, enquanto outros frameworks de interface de diferenciação em árvores não os utilizam. A diferência é muito mais rápida quando se usam números de sequência e Blazor tem a vantagem de uma etapa de compilação que lida automaticamente com os números de sequência para os programadores que criam
.razorficheiros.