0Pricing
C# Academy · 课时

组件通信与 DI

在父组件和子组件之间传递数据,使用级联参数,并将服务注入组件。

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

组件通信模式

Blazor 组件通过三个方向进行通信:父组件到子组件(参数)、子组件到父组件(EventCallback),以及跨组件树(CascadingParameters 或通过 DI 共享服务)。

父组件到子组件:参数

为组件属性添加 [Parameter] 特性,使其可以由父组件设置。父组件会将值作为组件标签上的 HTML 属性传入。

@* Child: Alert.razor *@
<div class="alert alert-@Type">
    @Message
</div>

@code {
    [Parameter] public string Type { get; set; } = "info"; // primary, warning, danger
    [Parameter, EditorRequired] public string Message { get; set; } = "";
}

@* Parent usage: *@
<Alert Type="danger" Message="Something went wrong!" />
<Alert Message="Record saved." />

子组件到父组件:EventCallback

子组件公开一个 EventCallback 参数。父组件提供一个处理程序方法。当某个事件发生时,子组件调用该方法。

@* Child: QuantityPicker.razor *@
<button @onclick="() => OnChanged.InvokeAsync(Value - 1)">-</button>
<span>@Value</span>
<button @onclick="() => OnChanged.InvokeAsync(Value + 1)">+</button>

@code {
    [Parameter] public int Value { get; set; } = 1;
    [Parameter] public EventCallback<int> OnChanged { get; set; }
}

@* Parent: *@
<QuantityPicker Value="@_qty" OnChanged="q => _qty = q" />
<p>Quantity: @_qty</p>

通过父组件进行兄弟组件通信

要让兄弟组件进行通信,请通过共同的父组件传递数据。父组件持有共享状态,并将其作为参数传递给子组件。

@* Parent holds shared state *@
<CategoryFilter OnChanged="c => _category = c" />
<ProductList Category="_category" />

@code {
    private string _category = "All";
}

@* CategoryFilter raises an event -> Parent updates _category
   -> ProductList re-renders with new Category *@

级联参数

使用 <CascadingValue> 包裹一个子树,即可将数据传递给所有后代组件,而不必让数据经过每个中间组件。

@* Wrap a subtree *@
<CascadingValue Value="_theme">
    <MainLayout />
</CascadingValue>

@code { private Theme _theme = new Theme { PrimaryColor = "#6366f1" }; }

@* Any descendant can receive it: *@
@code {
    [CascadingParameter]
    public Theme AppTheme { get; set; } = default!;
}

<h1 style="color: @AppTheme.PrimaryColor">Hello</h1>

使用 @inject 注入服务

使用 @inject 指令,将已注册的 DI 服务直接注入组件。框架会自动解析这些服务。

@inject IProductService ProductSvc
@inject ILogger<ProductList> Logger
@inject NavigationManager Nav

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

    protected override async Task OnInitializedAsync()
    {
        Logger.LogInformation("Loading products");
        _products = await ProductSvc.GetAllAsync();
    }

    private void GoToProduct(int id)
        => Nav.NavigateTo($"/products/{id}");
}

Blazor Server 中的作用域服务

在 Blazor Server 中,DI 作用域会持续整个 SignalR 连接的生命周期,而不仅仅是一次请求。请注意,在同一个电路中,所有组件共享作用域服务。

// Blazor Server: scoped = per circuit (connection lifetime)
builder.Services.AddScoped<CartService>();
// All components in the same browser tab share the SAME CartService

// This is GOOD for cart state (user's items persist across pages)
// This is BAD for request-scoped data — treat as session-like scope

// For true per-render-cycle scope, create your own:
using var scope = ScopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

使用 NavigationManager 进行路由

注入 NavigationManager,即可通过程序导航、读取当前 URL 并处理导航事件。

@inject NavigationManager Nav

@code {
    private void GoHome()         => Nav.NavigateTo("/");
    private void GoToProduct(int id) => Nav.NavigateTo($"/products/{id}");
    private void ForceReload()    => Nav.NavigateTo("/", forceLoad: true);

    protected override void OnInitialized()
    {
        // Read current URL
        var uri = Nav.Uri;
        var path = new Uri(uri).AbsolutePath;
    }
}

Blazor WASM 中的 HttpClient

在 Blazor WebAssembly 中,注入预先配置好的 HttpClient 来调用您的 API。在 Program.cs 中使用基地址注册它。

// Program.cs (Blazor WASM)
builder.Services.AddScoped(sp => new HttpClient
{
    BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});

// Component:
@inject HttpClient Http

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

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

实际应用:用户上下文服务

在应用的多个组件中注入作用域 UserContextService,这样每个组件都能看到相同的用户数据,而无需逐层传递属性。

public class UserContextService
{
    public string? Name { get; private set; }
    public bool IsAdmin { get; private set; }

    public async Task InitializeAsync(AuthenticationState auth)
    {
        var user = auth.User;
        Name = user.Identity?.Name;
        IsAdmin = user.IsInRole("Admin");
    }
}

// Registered as scoped:
builder.Services.AddScoped<UserContextService>();

// Used in any component:
@inject UserContextService UserCtx
<p>Hello, @UserCtx.Name @(UserCtx.IsAdmin ? "(Admin)" : "")</p>

快速检查

在 Blazor Server 中,一个“作用域”DI 服务的生命周期会持续多久?

回顾:组件通信与 DI

要点:

  • 父组件 → 子组件:使用 [参数] 属性;使用 [EditorRequired] 表示必需参数
  • 子组件 → 父组件:使用 EventCallback;会自动触发父组件的 StateHasChanged
  • 兄弟组件:通过父组件状态传递,或使用共享 DI 服务
  • 跨组件树:使用 CascadingValue/CascadingParameter 处理深层子树
  • 使用 @inject 注入服务;Blazor Server 的作用域服务持续整个电路生命周期
  • 使用 NavigationManager 进行程序化路由

常见问题解答

「组件通信与 DI」课时是免费的吗?

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

「组件通信与 DI」这节课中我会学到什么?

在父组件和子组件之间传递数据,使用级联参数,并将服务注入组件。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

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

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

「组件通信与 DI」课时需要多长时间?

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

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

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

此课程中的所有课时

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