0Pricing
C# Academy · 课时

Blazor 组件模型

创建 Razor 组件,了解组件生命周期,并使用参数和级联值。

Blazor 组件模型 是 CoddyKit 上的免费 C# Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

什么是 Blazor?

Blazor 让您可以使用 C# 而非 JavaScript 构建交互式 Web 用户界面。组件定义在 .razor 文件中,将 HTML 标记与 C# 逻辑结合起来。Blazor 可以在服务器上运行(Blazor Server),也可以通过 WebAssembly(Blazor WASM)在浏览器中运行。

您的第一个组件

Razor 组件是一个包含 HTML 标记和 C# @code 块的 .razor 文件。组件名称会成为 HTML 标签名称。

@* Greeting.razor *@
<h2>Hello, @Name!</h2>
<p>You have @Count messages.</p>

@code {
    [Parameter] public string Name { get; set; } = "World";
    private int Count = 5;
}

组件生命周期

Blazor 组件具有一个包含可重写钩子的生命周期。最常用的钩子是 OnInitializedAsync(加载数据)和 OnParametersSetAsync(响应参数变化)。

@code {
    private List<Product> Products = new();

    protected override async Task OnInitializedAsync()
    {
        // Called once on first render
        Products = await Http.GetFromJsonAsync<List<Product>>("/api/products")
                   ?? new();
    }

    protected override async Task OnParametersSetAsync()
    {
        // Called whenever parameters change
        await LoadDataAsync();
    }
}

参数:向组件传递数据

使用 [Parameter] 修饰属性,以便从父组件接收数据。参数会在渲染前设置,发生变化时可能触发重新渲染。

@* ProductCard.razor *@
<div class="card">
    <h4>@Product.Name</h4>
    <p>Price: @Product.Price.ToString("C")</p>
    <button @onclick="AddToCart">Add to Cart</button>
</div>

@code {
    [Parameter, EditorRequired]
    public Product Product { get; set; } = default!;

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

    private async Task AddToCart() =>
        await OnAddToCart.InvokeAsync(Product);
}

级联参数

级联参数会向下流经组件树,无需显式地逐层传递——非常适合主题、用户信息或共享状态。

@* App.razor — wraps the whole app *@
<CascadingValue Value="CurrentUser">
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <RouteView RouteData="routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
    </Router>
</CascadingValue>

@* Anywhere in the tree: *@
@code {
    [CascadingParameter]
    public UserInfo CurrentUser { get; set; } = default!;
}

渲染列表和条件内容

使用 Razor 指令 @if、@foreach 和 @for 渲染动态内容。请始终为列表项提供 @key,以便高效地进行差异比较。

@if (Products.Count == 0)
{
    <p>No products found.</p>
}
else
{
    <ul>
        @foreach (var product in Products)
        {
            <li @key="product.Id">
                <ProductCard Product="product" OnAddToCart="HandleAdd" />
            </li>
        }
    </ul>
}

RenderFragment 与 ChildContent

使用 RenderFragment 将标记作为参数传递——类似于 Vue 中的插槽或 React 中的子元素。约定使用的参数名称是 ChildContent。

@* Panel.razor *@
<div class="panel">
    <div class="panel-header">@Title</div>
    <div class="panel-body">@ChildContent</div>
</div>

@code {
    [Parameter] public string Title { get; set; } = "";
    [Parameter] public RenderFragment? ChildContent { get; set; }
}

@* Usage: *@
<Panel Title="Order Details">
    <p>Order #42 — Shipped</p>
</Panel>

使用 @ref 引用组件

使用 @ref 捕获对子组件的引用,以便从父组件调用其公共方法或读取其属性。

@* Parent.razor *@
<ConfirmDialog @ref="_dialog" OnConfirm="DeleteOrder" />
<button @onclick="() => _dialog!.Show()">Delete Order</button>

@code {
    private ConfirmDialog? _dialog;

    private async Task DeleteOrder()
    {
        await OrderService.DeleteAsync(CurrentOrderId);
        // Refresh UI...
    }
}

StateHasChanged 与手动重新渲染

Blazor 会在事件处理程序执行后自动重新渲染。对于事件处理程序之外发生的异步状态变化(例如来自后台计时器的变化),请手动调用 StateHasChanged()。

@implements IDisposable

<p>Time: @CurrentTime</p>

@code {
    private string CurrentTime = "";
    private Timer? _timer;

    protected override void OnInitialized()
    {
        _timer = new Timer(_ =>
        {
            CurrentTime = DateTime.Now.ToString("HH:mm:ss");
            InvokeAsync(StateHasChanged); // thread-safe re-render
        }, null, 0, 1000);
    }

    public void Dispose() => _timer?.Dispose();
}

使用 ShouldRender 优化性能

重写 ShouldRender() 以跳过不必要的重新渲染。当组件输出不会变化时返回 false,从而减少差异比较开销。

@code {
    private string _lastRenderedStatus = "";

    [Parameter] public string Status { get; set; } = "";

    protected override bool ShouldRender()
    {
        // Only re-render if Status actually changed
        if (Status == _lastRenderedStatus) return false;
        _lastRenderedStatus = Status;
        return true;
    }
}

真实场景:产品列表组件

一个完整的产品列表组件:它会在初始化时加载数据、处理加载状态,并将事件传递给父组件。

@inject HttpClient Http

@if (_loading)
{
    <p>Loading products...</p>
}
else
{
    @foreach (var p in _products)
    {
        <ProductCard Product="p" OnAddToCart="AddToCart" />
    }
}

@code {
    private List<Product> _products = new();
    private bool _loading = true;

    [Parameter] public EventCallback<Product> OnCartUpdated { get; set; }

    protected override async Task OnInitializedAsync()
    {
        _products = await Http.GetFromJsonAsync<List<Product>>("/api/products")
                   ?? new();
        _loading = false;
    }

    private async Task AddToCart(Product p) => await OnCartUpdated.InvokeAsync(p);
}

快速检查

Blazor 组件初始化时,应在哪个生命周期方法中从 API 加载数据?

回顾:Blazor 组件模型

要点:

  • .razor 文件将 HTML 标记与 @code 块中的 C# 混合在一起
  • [Parameter] 接收来自父组件的数据;EventCallback 向上传递事件
  • CascadingParameter 向下传递数据,无需逐层传递属性
  • RenderFragment/ChildContent 创建可组合的基于插槽的组件
  • 使用 OnInitializedAsync 加载初始数据;使用 StateHasChanged 手动重新渲染
  • ShouldRender() 跳过不必要的差异比较,以提升性能

常见问题解答

「Blazor 组件模型」课时是免费的吗?

是的 — 「Blazor 组件模型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「Blazor 组件模型」这节课中我会学到什么?

创建 Razor 组件,了解组件生命周期,并使用参数和级联值。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「Blazor 组件模型」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Blazor 组件模型
  2. 数据绑定与事件处理
  3. 组件通信与 DI
  4. Blazor 中的状态管理
← 返回 C# Academy