Blazor 从零开始:第 3 章 —— 可扩展的组件设计
·
2 分钟阅读
Available in:
中文
·
English
·
Español
·
Français
·
Deutsch
·
Português
·
한국어
·
日本語
·
Русский
·
العربية
·
हिन्दी
·
Polski
·
Türkçe
·
Bahasa Indonesia
·
Nederlands
欢迎来到 Blazor 从零开始 第 3 章。如果你还没看 第 2 章,建议先看完并准备好基础项目。
第 2 章我们让应用跑起来了;第 3 章我们要让它 更易维护。
当每个页面都变成巨大 .razor 文件时,Blazor 项目会很快失控。组件化可以避免这一点:更一致、更可复用、边界更清晰。
Blazor 组件到底是什么
组件本质上是一个 .razor 文件,它可以:
- 渲染标记
- 保存本地状态
- 通过参数接收输入
- 向父组件发出事件
- 渲染子内容
运行时,Blazor 会把每个组件当作一个小型状态机。状态变化后,框架会重新渲染并应用 DOM 差异。
所以你的目标是:输入清晰、行为可预测。
第一步:从职责单一的组件开始
创建 Components/Common/SectionHeader.razor:
<header class="section-header">
<h2>@Title</h2>
@if (!string.IsNullOrWhiteSpace(Subtitle))
{
<p>@Subtitle</p>
}
</header>
@code {
[Parameter] public string Title { get; set; } = string.Empty;
[Parameter] public string? Subtitle { get; set; }
}
在 Components/Pages/Home.razor 中使用:
@page "/"
<PageTitle>Blazor 从零开始</PageTitle>
<SectionHeader
Title="Blazor 从零开始"
Subtitle="第 3 章聚焦组件设计。" />
这个例子很小,但核心思想很关键:只看参数就应该能理解组件用途。
第二步:用参数显式表达行为
我们做一个可复用按钮组件。
Components/Common/AppButton.razor:
<button class="app-button @VariantCssClass" @onclick="OnClick">
@Text
</button>
@code {
[Parameter] public string Text { get; set; } = "Button";
[Parameter] public string Variant { get; set; } = "primary";
[Parameter] public EventCallback OnClick { get; set; }
private string VariantCssClass => Variant.ToLowerInvariant() switch
{
"secondary" => "app-button--secondary",
"danger" => "app-button--danger",
_ => "app-button--primary"
};
}
用法:
@code {
private int _savedCount;
private void Save()
{
_savedCount++;
}
}
<AppButton Text="Save" Variant="primary" OnClick="Save" />
<p>已保存 @_savedCount 次。</p>
重点在于“契约设计”:
- 参数要少且清晰
- 命名要显式,避免“魔法行为”
- 默认值要安全
第三步:用 RenderFragment 做布局组合
RenderFragment 允许父组件把一段 UI 传给子组件。
创建 Components/Common/Card.razor:
<article class="card">
<header class="card__header">@Title</header>
<section class="card__body">
@ChildContent
</section>
</article>
@code {
[Parameter] public string Title { get; set; } = string.Empty;
[Parameter] public RenderFragment? ChildContent { get; set; }
}
用法:
<Card Title="Roadmap">
<ul>
<li>组件</li>
<li>Data binding</li>
<li>Routing</li>
</ul>
</Card>
这个模式可以在不重复外层包装标记的前提下,保持页面结构一致。
第四步:优先组合,而不是巨型页面
当页面持续变大时,按职责拆分:
ProfileSummary:顶部信息块ProfileStats:指标展示ProfileActivityList:最近活动
这样页面就变成“编排层”,而不是塞满实现细节的地方。
第五步:保持标记与逻辑的平衡
简单组件使用内联 @code 就够了。
复杂组件建议使用 code-behind:
UserCard.razorUserCard.razor.cs
这样既能保持标记可读,也能让 C# 逻辑更清晰。
第六步:实用的目录结构
一个较容易扩展的结构:
Components/Pages/-> 可路由页面Components/Layout/-> 应用壳层与导航Components/Common/-> 通用共享组件Components/Features/<FeatureName>/-> 功能域组件
Blazor 初期常见问题
- 参数过多,而不是抽出专用模型
- 把业务规则直接写进页面组件
- 做出一个数百行的“上帝组件”
- 重复粘贴标记,而不是提取可复用组件
下一章
第 4 章我们将进入 数据绑定与事件:@bind、事件处理、双向绑定的权衡,以及如何让状态保持可预测。