关于 blazor
面向 Blazor 交互式 Web 界面开发的技能,涵盖渲染模式选择、组件设计、状态处理、预渲染、性能和 JavaScript 互操作,也涉及连接 AI 后端。
主要功能
- 01选择 Static、Server、WebAssembly 或 Auto 渲染模式
- 02设计可复用组件与状态管理
- 03处理预渲染和状态持久化
- 04使用 JavaScript 互操作
- 05优化列表渲染与性能
适用场景
- 01用 C# 构建交互式 Web 界面
- 02按 SEO、实时性或离线需求选择渲染模式
- 03避免预渲染与水合期间重复获取数据
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ managedcode/ dotnet-skills --skill blazor安装后在 Claude Code 中输入/blazor确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
manifest.jsonreferences/anti-patterns.mdpatterns.md详细介绍
Blazor
适用场景
- 使用 C# 而非 JavaScript 构建交互式 Web UI
- 在 Server、WebAssembly 或 Auto 渲染模式之间进行选择
- 设计组件层次结构和状态管理
- 处理预渲染和水合
- 必要时与 JavaScript 集成
- 将 Blazor UI 连接到 AI 后端,并使用 .NET 构建 AI Agent
文档
参考资料
- 使用 .NET 构建 AI Agent - 面向生产环境的 .NET AI Agent 架构和实现模式。
- .NET AI Agent 开发团队 - 生产级 .NET AI Agent 工程和交付服务。
- patterns.md - 详细的组件模式、状态管理策略和 JS 互操作技术
- anti-patterns.md - 常见的 Blazor 错误及其规避方法
渲染模式(.NET 8+)
| 模式 | 运行位置 | 适用场景 |
|---|---|---|
Static | 服务端(无交互性) | SEO 页面、营销内容 |
InteractiveServer | 通过 SignalR 在服务端运行 | 实时应用、轻量客户端 |
InteractiveWebAssembly | 通过 WASM 在浏览器中运行 | 支持离线、客户端负载较重 |
InteractiveAuto | 先在服务端运行,然后在 WASM 中运行 | 兼具两者优势 |
应用渲染模式
@* Per-component *@
@rendermode InteractiveServer
@* Or in App.razor for global *@
<Routes @rendermode="InteractiveAuto" />
InteractiveAuto 架构
First Request:
Browser → Server (Interactive Server) → Fast response
Subsequent Requests:
Browser → WASM (downloaded in background) → No server needed
工作流
-
根据需求选择渲染模式:
- 需要 SEO?从 Static 或预渲染开始
- 需要实时交互?使用 InteractiveServer
- 需要离线支持?使用 InteractiveWebAssembly
- 两者都需要?使用 InteractiveAuto
-
设计可复用组件:
- 组件小巧、专注
- 使用参数进行自定义
- 使用事件进行通信
-
正确处理状态:
- 组件状态保存在组件内
- 使用服务(DI)共享状态
- 使用
[PersistentState]在预渲染期间持久化状态
-
在两种环境中验证(针对 Auto 模式)
当前上游说明
- 将
dotnet/aspnetcorev10.0.11视为维护版本。此版本修复了客户端持久化的电路状态过期后无法恢复的问题;升级时请重新运行重连、持久化状态过期、渲染模式以及 interactive-auto 客户端/服务端拆分测试。 - 2026 年 8 月的 ASP.NET Core 概述仍将 Blazor 定位为组件 UI 方案。维护更新本身不会改变组件或渲染模式架构。
组件模式
基本组件
@* Counter.razor *@
<button @onclick="IncrementCount">
Clicked @count times
</button>
@code {
private int count = 0;
[Parameter]
public int InitialCount { get; set; } = 0;
protected override void OnInitialized()
{
count = InitialCount;
}
private void IncrementCount() => count++;
}
参数和事件回调
@* Parent.razor *@
<ChildComponent Value="@value" ValueChanged="@OnValueChanged" />
@* ChildComponent.razor *@
@code {
[Parameter] public string Value { get; set; } = "";
[Parameter] public EventCallback<string> ValueChanged { get; set; }
private async Task UpdateValue(string newValue)
{
await ValueChanged.InvokeAsync(newValue);
}
}
状态持久化(.NET 8+)
@* Prevents double-fetch during prerender + hydration *@
@code {
[PersistentState]
public List<Product> Products { get; set; } = [];
protected override async Task OnInitializedAsync()
{
// Only fetches once, persisted across prerender
Products ??= await Http.GetFromJsonAsync<List<Product>>("api/products");
}
}
Auto 模式的数据访问模式
// Shared interface
public interface IProductService
{
Task<List<Product>> GetProductsAsync();
}
// Server implementation (direct DB access)
public class ServerProductService : IProductService
{
private readonly AppDbContext _db;
public async Task<List<Product>> GetProductsAsync()
=> await _db.Products.ToListAsync();
}
// Client implementation (HTTP call)
public class ClientProductService : IProductService
{
private readonly HttpClient _http;
public async Task<List<Product>> GetProductsAsync()
=> await _http.GetFromJsonAsync<List<Product>>("api/products");
}
// Registration
// Server: builder.Services.AddScoped<IProductService, ServerProductService>();
// Client: builder.Services.AddScoped<IProductService, ClientProductService>();
应避免的反模式
| 反模式 | 不足之处 | 更好的做法 |
|---|---|---|
| 组件过大 | 难以维护,渲染速度慢 | 拆分成更小的组件 |
| 在 WASM 中直接访问数据库 | 浏览器中没有数据库 | 使用 HTTP API |
忽略 ShouldRender | 不必要的重新渲染 | 按需重写 |
| 在 Server 中进行同步 JS 互操作 | 阻塞 SignalR 电路 | 异步使用 IJSRuntime |
| 没有错误边界 | 一个错误就会导致应用崩溃 | 使用 <ErrorBoundary> |
| 忘记预渲染状态 | 导致 API 调用两次 | 使用 [PersistentState] |
性能最佳实践
-
虚拟化大型列表:
<Virtualize Items="@products" Context="product"> <ProductCard Product="@product" /> </Virtualize> -
使用
@key进行列表差异比对:@foreach (var item in items) { <ItemComponent @key="item.Id" Item="@item" /> } -
对频繁触发的事件进行防抖处理:
private Timer? _debounceTimer; private void OnInput(ChangeEventArgs e) { _debounceTimer?.Dispose(); _debounceTimer = new Timer(_ => InvokeAsync(DoSearch), null, 300, Timeout.Infinite); } -
延迟加载程序集(WASM):
var assemblies = await LazyAssemblyLoader .LoadAssembliesAsync(["MyHeavyFeature.wasm"]);
JS 互操作
从 C# 调用 JavaScript
@inject IJSRuntime JS
await JS.InvokeVoidAsync("alert", "Hello from Blazor!");
var result = await JS.InvokeAsync<string>("prompt", "Enter name:");
从 JavaScript 调用 C#
[JSInvokable]
public static string GetMessage() => "Hello from C#!";
DotNet.invokeMethodAsync('MyAssembly', 'GetMessage')
.then(result => console.log(result));
交付
- 具有适当渲染模式的交互式 Blazor 组件
- 高效的状态管理和数据流
- 正确处理预渲染场景
- 通过虚拟化实现高性能列表渲染
验证
- 组件在所选模式下正确渲染
- 状态在预渲染和水合过程中正确保留
- 无不必要的重新渲染(使用浏览器工具检查)
- JS 互操作在 Server 和 WASM 中均可正常工作
- 错误边界能够捕获组件故障
- Auto 模式在两种环境中均可正常工作
评论
支持 Markdown