it

C# Blazor Best Practices ที่ต้องรู้ — พัฒนา Web

C# Blazor Best Practices ที่ต้องรู้ — พัฒนา Web

Blazor Best Practices

C# Blazor Best Practices ที่ต้องรู้ — พัฒนา Web

C# Blazor Best Practices Component Design State Management Performance Authentication Testing Blazor Server WebAssembly Hybrid .NET 8 Production

ModeExecutionLatencyOfflineSEOเหมาะกับ
Blazor ServerServer (SignalR)ต่ำไม่ได้ดี (SSR)Internal apps, real-time
Blazor WASMBrowser (WebAssembly)สูง (initial)ได้ (PWA)ต้อง Pre-renderSPA, offline apps
Blazor AutoServer → WASMต่ำ → ต่ำได้ (หลัง switch)ดี.NET 8 recommended
Blazor HybridNative (MAUI)ต่ำมากได้N/ADesktop/Mobile app

Component Patterns

=== Blazor Component Patterns ===

ProductCard.razor — Single Responsibility Component

@code {

[Parameter] public ProductDto Product { get; set; } = null!;

[Parameter] public EventCallback<int> OnAddToCart { get; set; }

[Parameter] public RenderFragment? ChildContent { get; set; }

private async Task HandleAddToCart()

{

await OnAddToCart.InvokeAsync(Product.Id);

}

}

GenericList.razor — Template Component

@typeparam TItem

@code {

[Parameter] public IEnumerable<TItem> Items { get; set; } = [];

[Parameter] public RenderFragment<TItem> ItemTemplate { get; set; } = null!;

[Parameter] public RenderFragment? EmptyTemplate { get; set; }

}

<div>

@if (Items.Any())

{

@foreach (var item in Items)

{

@ItemTemplate(item)

}

}

else

{

@EmptyTemplate

}

</div>

Usage:

<GenericList Items="products" Context="product">

<ItemTemplate>

<ProductCard Product="product" OnAddToCart="AddToCart" />

</ItemTemplate>

<EmptyTemplate>

<p>No products found</p>

</EmptyTemplate>

เนื้อหาเกี่ยวข้อง — แนะนำให้อ่าน ครปไทย — คู่มือฉบับสมบูรณ์ 2026

</GenericList>

from dataclasses import dataclass

@dataclass

class ComponentPattern:

pattern: str

use_case: str

example: str

benefit: str

patterns = [

ComponentPattern("Parameter + EventCallback", "Parent-child communication",

"[Parameter] public string Title { get; set; }", "Clear data flow"),

ComponentPattern("CascadingValue", "Deep component tree data",

แนะนำเพิ่มเติม — เรียนเทรดกับ iCafeForex

"<CascadingValue Value='theme'>", "Avoid prop drilling"),

ComponentPattern("RenderFragment", "Template/slot components",

"[Parameter] public RenderFragment ChildContent", "Flexible composition"),

ComponentPattern("@typeparam", "Generic reusable components",

"@typeparam TItem on GenericList", "Type-safe reusable lists"),

ComponentPattern("@key", "Efficient list rendering",

"@foreach with @key='item.Id'", "Prevent full list re-render"),

ComponentPattern("Base Component", "Shared logic inheritance",

"ComponentBase with common methods", "DRY principle"),

]

print("=== Component Patterns ===")

for p in patterns:

print(f" [{p.pattern}] Use: {p.use_case}")

print(f" Example: {p.example}")

print(f" Benefit: {p.benefit}")

State and Performance

=== State Management and Performance ===

State Container Pattern

public class CartState

{

public List&lt;CartItem&gt; Items { get; private set; } = [];

public decimal Total => Items.Sum(i => i.Price * i.Quantity);

public event Action? OnChange;

public void AddItem(CartItem item)

{

var existing = Items.FirstOrDefault(i => i.ProductId == item.ProductId);

if (existing != null)

existing.Quantity += item.Quantity;

else

เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง TTS Coqui Feature Flag Management —

Items.Add(item);

NotifyStateChanged();

}

private void NotifyStateChanged() => OnChange?.Invoke();

}

Register in DI

builder.Services.AddScoped&lt;CartState&gt;();

Use in Component

@inject CartState Cart

@implements IDisposable

protected override void OnInitialized()

{

Cart.OnChange += StateHasChanged;

}

public void Dispose()

{

Cart.OnChange -= StateHasChanged;

}

Performance: Virtualization

&lt;Virtualize Items="products" Context="product"&gt;

&lt;ProductCard Product="product" /&gt;

แนะนำเพิ่มเติม — หนังสือเทรดที่ SiamCafeBook

&lt;/Virtualize&gt;

Performance: ShouldRender override

protected override bool ShouldRender()

{

return _hasChanged;

}

@dataclass

class PerfTip:

C# Blazor Best Practices ที่ต้องรู้ — พัฒนา Web

tip: str

problem: str

solution: str

impact: str

tips = [

PerfTip("Use @key", "List re-renders all items on change",

"@key='item.Id' on each item", "50-90% fewer DOM updates"),

PerfTip("Virtualize", "Rendering 1000+ items causes lag",

"<Virtualize Items='list'> renders only visible", "Instant scroll for large lists"),

PerfTip("ShouldRender", "Component re-renders unnecessarily",

"Override ShouldRender() return false when no change", "Eliminate wasted renders"),

เนื้อหาเกี่ยวข้อง — อ่านต่อ: Grafana Loki LogQL Multi-tenant Design

PerfTip("Dispose handlers", "Event handlers cause memory leaks",

"Implement IDisposable, unsubscribe in Dispose()", "Prevent memory growth"),

PerfTip("Lazy Assembly", "WASM downloads all DLLs upfront",

"LazyAssemblyLoader for rarely-used pages", "Reduce initial download 30-60%"),

PerfTip("Pre-render SSR", "WASM shows blank before JS loads",

"Use InteractiveAuto for SSR + WASM", "Instant first paint"),

]

print("\n=== Performance Tips ===")

for t in tips:

print(f" [{t.tip}] Problem: {t.problem}")

print(f" Fix: {t.solution}")

print(f" Impact: {t.impact}")

Auth and Testing

=== Authentication and Testing ===

Authentication Setup (.NET 8)

builder.Services.AddAuthentication()

.AddCookie()

.AddJwtBearer(options => {

options.TokenValidationParameters = new() {

ValidateIssuer = true,

ValidIssuer = "https://myapp.com",

ValidateAudience = true,

ValidAudience = "myapp-api",

IssuerSigningKey = new SymmetricSecurityKey(key),

};

});

AuthorizeView in Razor

&lt;AuthorizeView Roles="Admin"&gt;

&lt;Authorized&gt;

&lt;AdminPanel /&gt;

&lt;/Authorized&gt;

&lt;NotAuthorized&gt;

&lt;p&gt;Access Denied&lt;/p&gt;

&lt;/NotAuthorized&gt;

&lt;/AuthorizeView&gt;

[Authorize] on page

@page "/admin"

@attribute [Authorize(Roles = "Admin")]

bUnit Testing

[Fact]

เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง Snowflake Snowpark Backup Recovery Strategy

public void ProductCard_ShowsName()

{

using var ctx = new TestContext();

var product = new ProductDto { Id = 1, Name = "Test", Price = 99 };

var cut = ctx.RenderComponent&lt;ProductCard&gt;(

parameters => parameters.Add(p => p.Product, product));

cut.Find("h3").TextContent.ShouldBe("Test");

}

[Fact]

public void AddToCart_FiresCallback()

{

using var ctx = new TestContext();

var clicked = false;

var cut = ctx.RenderComponent&lt;ProductCard&gt;(parameters => parameters

.Add(p => p.Product, product)

.Add(p => p.OnAddToCart, EventCallback.Factory.Create&lt;int&gt;(this, id => clicked = true)));

cut.Find("button").Click();

clicked.ShouldBeTrue();

}

@dataclass

class BestPractice:

category: str

practice: str

why: str

practices = [

BestPractice("Component", "แยก Component เล็ก ทำหน้าที่เดียว", "Reusable, testable, maintainable"),

BestPractice("State", "ใช้ State Container Pattern + DI", "Centralized state, predictable updates"),

BestPractice("Performance", "ใช้ @key Virtualize ShouldRender", "Smooth UI, less memory"),

BestPractice("Auth", "Server-side validation เสมอ ไม่พึ่ง UI", "Security cannot be client-side only"),

BestPractice("Testing", "ใช้ bUnit test ทุก Component", "Catch bugs early, safe refactor"),

BestPractice("Error", "ใช้ ErrorBoundary wrap components", "Graceful error handling"),

BestPractice("DI", "Register services ด้วย Scoped lifetime", "Correct lifecycle for Blazor"),

BestPractice("Dispose", "Implement IDisposable ทุก Component ที่ subscribe", "Prevent memory leaks"),

]

print("Best Practices:")

for b in practices:

print(f" [{b.category}] {b.practice}")

print(f" Why: {b.why}")

เคล็ดลับ

  • @key: ใช้ @key ทุก List Rendering ป้องกัน Re-render ทั้งหมด
  • Dispose: Implement IDisposable ทุก Component ที่ Subscribe Event
  • Auto Mode: ใช้ InteractiveAuto ใน .NET 8 ได้ SSR + WASM อัตโนมัติ
  • bUnit: เขียน bUnit Test ทุก Component ตรวจ Render + Events
  • ErrorBoundary: Wrap Component ด้วย ErrorBoundary ป้องกัน Crash

Blazor คืออะไร

Web Framework Microsoft C# แทน JavaScript Interactive Web App Server SignalR WebAssembly Browser Hybrid MAUI Razor Component .NET 8 Auto Render

XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง