Bagikan melalui


skenario lanjutan ASP.NET Core Blazor (konstruksi pohon render)

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Artikel ini menjelaskan skenario lanjutan untuk membangun Blazor pohon render secara manual dengan RenderTreeBuilder.

Peringatan

Penggunaan RenderTreeBuilder untuk membuat komponen adalah skenario tingkat lanjut. Komponen cacat (misalnya, tag markup yang tidak tertutup) dapat mengakibatkan perilaku yang tidak terdefinisi. Perilaku yang tidak ditentukan termasuk penyajian konten yang rusak, hilangnya fitur aplikasi, dan keamanan yang disusupi.

Membangun pohon render secara manual (RenderTreeBuilder)

RenderTreeBuilder menyediakan metode untuk memanipulasi komponen dan elemen, termasuk membangun komponen secara manual dalam kode C#.

Pertimbangkan komponen berikut PetDetails , yang dapat dirender secara manual di komponen lain.

PetDetails.razor:

<h2>Pet Details</h2>

<p>@PetDetailsQuote</p>

@code
{
    [Parameter]
    public string? PetDetailsQuote { get; set; }
}

Dalam komponen berikut BuiltContent , perulangan dalam CreateComponent metode menghasilkan tiga PetDetails komponen.

Dalam RenderTreeBuilder metode dengan nomor urut, nomor urutan adalah nomor baris kode sumber. Blazor Algoritma perbedaan bergantung pada nomor urut yang sesuai dengan baris kode yang berbeda, bukan pemanggilan panggilan yang berbeda. Saat membuat komponen dengan RenderTreeBuilder metode, hardcode argumen untuk nomor urut. Menggunakan perhitungan atau penghitung untuk menghasilkan nomor urut dapat menyebabkan performa yang buruk. Untuk informasi selengkapnya, lihat bagian Urutan angka terkait dengan nomor baris kode dan bukan urutan eksekusi.

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

Peringatan

Jenis dalam Microsoft.AspNetCore.Components.RenderTree memungkinkan pemrosesan hasil operasi penyajian. Ini adalah detail internal implementasi Blazor kerangka kerja. Jenis-jenis ini harus dianggap tidak stabil dan dapat berubah dalam rilis mendatang.

Nomor urutan terkait dengan nomor baris kode dan bukan urutan eksekusi

Razor file komponen (.razor) selalu dikompilasi. Mengeksekusi kode yang dikompilasi memiliki potensi keuntungan daripada menginterpretasikan kode karena langkah kompilasi yang menghasilkan kode yang dikompilasi dapat digunakan untuk menyuntikkan informasi yang meningkatkan performa aplikasi pada runtime.

Contoh utama peningkatan ini melibatkan nomor urut. Nomor urutan menunjukkan ke runtime mana output berasal dari baris kode yang berbeda dan diurutkan. Runtime menggunakan informasi ini untuk menghasilkan perbedaan pohon yang efisien dalam waktu linier, yang jauh lebih cepat dari yang biasanya dimungkinkan untuk algoritma diff pohon umum.

Pertimbangkan file komponen berikut Razor (.razor):

@if (someFlag)
{
    <text>First</text>
}

Second

Konten markup dan teks sebelumnya dikompilasi Razor ke dalam kode C# yang mirip dengan yang berikut ini:

if (someFlag)
{
    builder.AddContent(0, "First");
}

builder.AddContent(1, "Second");

Ketika kode dijalankan untuk pertama kalinya dan someFlag adalah true, penyusun menerima urutan dalam tabel berikut.

Sequence Jenis Data
0 Node teks First
1 Node teks Detik

Bayangkan itu someFlag menjadi false dan markup dirender lagi. Kali ini, penyusun menerima urutan dalam tabel berikut.

Sequence Jenis Data
1 Node teks Detik

Ketika runtime melakukan diff, runtime melihat bahwa item pada urutan 0 dihapus, sehingga menghasilkan skrip edit sepele berikut dengan satu langkah:

  • Hapus simpul teks pertama.

Masalah dengan menghasilkan nomor urutan secara terprogram

Bayangkan sebaliknya bahwa Anda menulis logika pembuat pohon render berikut:

var seq = 0;

if (someFlag)
{
    builder.AddContent(seq++, "First");
}

builder.AddContent(seq++, "Second");

Output pertama tercermin dalam tabel berikut.

Sequence Jenis Data
0 Node teks First
1 Node teks Detik

Hasil ini identik dengan kasus sebelumnya, sehingga tidak ada masalah negatif. someFlag ada false di penyajian kedua, dan output terlihat dalam tabel berikut.

Sequence Jenis Data
0 Node teks Detik

Kali ini, algoritma diff melihat bahwa dua perubahan telah terjadi. Algoritma menghasilkan skrip edit berikut:

  • Ubah nilai simpul teks pertama menjadi Second.
  • Hapus simpul teks kedua.

Menghasilkan nomor urut telah kehilangan semua informasi yang berguna tentang di mana if/else cabang dan perulangan ada dalam kode asli. Ini menghasilkan perbedaan dua kali lebih lama dari sebelumnya.

Ini adalah contoh sepele. Dalam kasus yang lebih realistis dengan struktur yang kompleks dan sangat bersarang, dan terutama dengan perulangan, biaya performa biasanya lebih tinggi. Alih-alih segera mengidentifikasi blok perulangan atau cabang mana yang telah dimasukkan atau dihapus, algoritma diff harus berulang jauh ke dalam pohon render. Ini biasanya mengakibatkan pembuatan skrip edit yang lebih lama karena algoritma diff salah diformat tentang bagaimana struktur lama dan baru berhubungan satu sama lain.

Panduan dan kesimpulan

  • Performa aplikasi menderita jika angka urutan dihasilkan secara dinamis.
  • Informasi yang diperlukan tidak ada untuk mengizinkan kerangka kerja menghasilkan nomor urut secara otomatis pada runtime kecuali informasi diambil pada waktu kompilasi.
  • Jangan menulis blok panjang logika yang diimplementasikan RenderTreeBuilder secara manual. Lebih suka .razor file dan izinkan pengkompilasi untuk menangani nomor urut. Jika Anda tidak dapat menghindari logika manual RenderTreeBuilder , bagi blok kode panjang menjadi potongan yang lebih kecil yang dibungkus dalam OpenRegion/CloseRegion panggilan. Setiap wilayah memiliki ruang angka urutan terpisah sendiri, sehingga Anda dapat memulai ulang dari nol (atau angka arbitrer lainnya) di dalam setiap wilayah.
  • Jika angka urutan dikodekan secara permanen, algoritma diff hanya mengharuskan angka urutan tersebut meningkat nilainya. Nilai awal dan celah tidak relevan. Salah satu opsi yang sah adalah menggunakan nomor baris kode sebagai nomor urutan, atau mulai dari nol dan bertambah satu atau ratusan (atau interval pilihan apa pun).
  • Untuk perulangan, angka urutan harus meningkat dalam kode sumber Anda, bukan dalam hal perilaku runtime. Fakta bahwa, pada runtime, angka yang diulang adalah bagaimana sistem yang berbeda menyadari bahwa Anda berada dalam perulangan.
  • Blazor menggunakan nomor urut, sementara kerangka kerja UI diffing pohon lainnya tidak menggunakannya. Pembedaan jauh lebih cepat ketika nomor urutan digunakan, dan Blazor memiliki keuntungan dari langkah kompilasi yang berkaitan dengan nomor urut secara otomatis untuk pengembang yang menulis .razor file.