跳到主要内容

6.2.1 Blazor 组件基础

Blazor 组件把 UI、参数、事件和渲染逻辑封装在 .razor 文件中。它适合 .NET 开发者用 C# 构建交互式前端。

学习目标

  • 能创建一个可复用 Blazor 组件。
  • 能使用 [Parameter] 接收父组件数据。
  • 能使用 EventCallback 把用户操作传回父组件。
  • 能区分展示组件和承载状态的页面组件。

应用场景

  • 任务卡片、表单输入、分页器、筛选器等可复用 UI。
  • 管理后台、内部系统、数据录入应用。
  • 希望前后端都使用 C# 的团队项目。

核心概念

概念说明
组件.razor 文件,封装 UI 和交互逻辑
参数父组件传给子组件的数据
事件回调子组件通知父组件发生了什么
状态影响渲染结果的数据

案例:任务卡片组件

TodoCard.razor

<article class="todo-card @(Todo.IsCompleted ? "todo-card--done" : null)">
<h3>@Todo.Title</h3>

@if (!string.IsNullOrWhiteSpace(Todo.Description))
{
<p>@Todo.Description</p>
}

<button disabled="@Todo.IsCompleted" @onclick="CompleteAsync">
@(Todo.IsCompleted ? "已完成" : "标记完成")
</button>
</article>

@code {
[Parameter, EditorRequired]
public required TodoItemDto Todo { get; set; }

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

private Task CompleteAsync() => OnComplete.InvokeAsync(Todo.Id);
}

TodoItemDto.cs

public sealed record TodoItemDto(
int Id,
string Title,
string? Description,
bool IsCompleted);

示例:父组件使用子组件

@page "/todos"

<h1>任务列表</h1>

@foreach (var todo in todos)
{
<TodoCard Todo="todo" OnComplete="HandleCompleteAsync" />
}

@code {
private List<TodoItemDto> todos =
[
new(1, "学习 Minimal API", "完成第一个接口", false),
new(2, "学习 Blazor 组件", null, false),
];

private Task HandleCompleteAsync(int id)
{
var index = todos.FindIndex(todo => todo.Id == id);
if (index >= 0)
{
var current = todos[index];
todos[index] = current with {IsCompleted = true};
}

return Task.CompletedTask;
}
}

重点难点

  • 子组件不要直接修改父组件的集合状态,应通过事件回调通知父组件。
  • [EditorRequired] 是开发期提示,不替代运行时校验。
  • 组件过大时要拆分展示、表单和数据加载职责。
  • 异步事件处理要返回 Task,不要使用 async void

常见误区

误区推荐做法
所有 UI 都写在一个页面里提取可复用组件并用参数传值
子组件直接调用全局状态优先通过参数和事件形成清晰数据流
忽略加载和错误状态为 API 调用设计 loading、empty、error 三种状态

练习

  • TodoCard 增加删除按钮和 OnDelete 回调。
  • 增加一个 Priority 字段,并用 CSS 显示不同优先级。
  • 把示例中的内存数据替换为调用后端 API。