news 2026/4/3 5:47:00

ASP.NET Core Blazor 核心功能一:Blazor依赖注入与状态管理指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ASP.NET Core Blazor 核心功能一:Blazor依赖注入与状态管理指南

一、依赖注入基础

Blazor 提供了强大的依赖注入(Dependency Injection, DI)功能,用于将服务以解耦的方式注入到组件中,它帮助我们实现松耦合的代码设计,提高可测试性和可维护性。

什么是依赖注入?

依赖注入是一种设计模式,它允许类从外部接收其依赖项,而不是自己创建它们。在 Blazor 中,这意味着组件不需要知道如何创建服务,而是通过构造函数或属性接收这些服务。

二、注册和使用服务

1、创建自定义服务

1. 定义服务接口

复制代码

public interface ICounterService

{

int Increment(int currentValue);

int Decrement(int currentValue);

void Reset();

}

复制代码

2. 实现服务

复制代码

public class CounterService : ICounterService

{

public int Increment(int currentValue)

{

return currentValue + 1;

}

public int Decrement(int currentValue)

{

return currentValue - 1;

}

public void Reset()

{

// 重置逻辑

}

}

复制代码

2、注册服务

在 Program.cs 文件中配置服务容器:

复制代码

var builder = WebAssemblyHostBuilder.CreateDefault(args);

builder.RootComponents.Add<App>("#app");

// 注册服务

builder.Services.AddSingleton<ICounterService, CounterService>();

builder.Services.AddScoped<IUserService, UserService>();

builder.Services.AddTransient<IEmailService, EmailService>();

// 注册内置服务

builder.Services.AddLocalStorage();

builder.Services.AddAuthorizationCore();

await builder.Build().RunAsync();

复制代码

3、服务生命周期

Blazor 支持三种服务生命周期:

Singleton:整个应用生命周期内只有一个实例

Scoped:每个用户会话有一个实例(Blazor Server)或每个浏览器标签页(Blazor WebAssembly)

Transient:每次请求时创建新实例

4、在组件中使用依赖注入

1. 使用 [Inject] 特性

复制代码

@page "/counter"

@inject ICounterService CounterService

<h3>Counter</h3>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {

private int currentCount = 0;

private void IncrementCount()

{

currentCount = CounterService.Increment(currentCount);

}

}

复制代码

2. 在代码中使用注入的服务

复制代码

@page "/user-profile"

@inject IUserService UserService

@inject NavigationManager Navigation

<h3>User Profile</h3>

@if (user != null)

{

<div>

<p>Name: @user.Name</p>

<p>Email: @user.Email</p>

</div>

}

@code {

private User user;

protected override async Task OnInitializedAsync()

{

user = await UserService.GetCurrentUserAsync();

}

private async Task UpdateProfile()

{

await UserService.UpdateUserAsync(user);

Navigation.NavigateTo("/success");

}

}

复制代码

5、高级依赖注入用法

1. 工厂模式注册

builder.Services.AddSingleton<ICounterService>(provider =>

{

// 复杂的创建逻辑

return new CounterService();

});

2. 选项模式

复制代码

// 配置选项类

public class ApiOptions

{

public string BaseUrl { get; set; }

public int TimeoutSeconds { get; set; }

}

// 注册选项

builder.Services.Configure<ApiOptions>(options =>

{

options.BaseUrl = "https://api.example.com";

options.TimeoutSeconds = 30;

});

// 在服务中使用

public class ApiService

{

private readonly ApiOptions _options;

public ApiService(IOptions<ApiOptions> options)

{

_options = options.Value;

}

}

复制代码

3. 条件注册

#if DEBUG

builder.Services.AddSingleton<ILogger, DebugLogger>();

#else

builder.Services.AddSingleton<ILogger, ProductionLogger>();

#endif

三、组件状态管理

在Blazor开发中,状态管理是构建交互式Web应用的核心挑战。无论是简单的计数器组件还是复杂的实时协作系统,选择合适的状态管理方案直接影响应用性能和可维护性。

1、理解Blazor中的状态管理

状态是指应用程序或组件在某一时刻的数据或信息。例如,一个计数器组件可以有一个表示当前计数值的状态,一个表单组件可以有一个表示用户输入的状态,一个购物车组件可以有一个表示选中商品的状态等。状态管理是指如何存储、更新、获取和传递这些数据或信息。

在Blazor中,每个组件都有自己的私有状态,它只能被该组件访问和修改。如果要将状态从一个组件传递给另一个组件,或者在多个组件之间共享状态,就需要使用一些技术或模式来实现。下面我们将介绍一些常见的方法。

2、组件内状态:最简单的状态管理

Blazor组件最基础的状态管理方式是使用组件内部的字段或属性保存状态。这种模式适用于状态仅在单个组件内部使用且无需共享的场景,如计数器、表单输入等基础交互。

复制代码

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {

private int currentCount = 0;

private void IncrementCount()

{

currentCount++;

}

}

复制代码

上述代码展示了典型的组件内状态模式,currentCount字段存储计数器状态,IncrementCount方法修改状态并自动触发UI重新渲染。这种模式的优势在于实现简单、零外部依赖,适合快速开发独立功能组件。

3、父子组件通信:参数和事件回调

如果要将父组件的状态传递给子组件,或者从子组件获取更新后的状态,可以使用参数和属性来实现。

参数是指父组件向子组件传递数据或信息的方式。参数可以是任意类型的值,例如字符串、数字、布尔值、对象、委托等。要定义一个参数,需要在子组件中使用[Parameter]特性来标记一个公共属性,并且该属性的类型必须与父组件传递的值相同。例如:

这样就定义了一个名为Counter的参数,在子组件中可以使用以下语法来获取它的值:

<p>The counter value is @Counter</p>

在父组件中,可以使用以下语法来为参数赋值:

<CounterComponent Counter="@currentCount" />

@code {

private int currentCount = 0;

}

这样就将父组件中的变量currentCount作为参数值传递给了子组件。如果要实现从父到子单向绑定。

属性是指子组件向父组件传递数据或信息的方式。属性可以是任意类型的值,但通常是一个事件回调(EventCallback)或一个动作(Action),用于在子组件中触发父组件定义的一个方法,从而将数据或信息传递给父组件。要定义一个属性,需要在子组件中使用[Parameter]特性来标记一个公共属性,并且该属性的类型必须是EventCallback<T>或Action<T>,其中T是要传递的数据或信息的类型。例如:

复制代码

<h3>CounterComponent</h3>

<p>The counter value is @Counter</p>

<button @onclick="CounterChangedFromChild">Update Counter from Child</button>

@code {

[Parameter]

public int Counter { get; set; }

[Parameter]

public EventCallback<int> OnCounterChanged { get; set; }

private async Task CounterChangedFromChild()

{

Counter++;

await OnCounterChanged.InvokeAsync(Counter);

}

}

复制代码

以上例子中就定义了一个名为OnCounterChanged的属性,将子组件中的变量Counter作为参数传递给了父组件。在父组件中,可以使用以下语法来为属性赋值:

<CounterComponent OnCounterChanged="HandleCounterChanged" />

这样就将父组件中定义的一个方法名作为属性值传递给了子组件。该方法必须接受一个与属性类型相同的参数,并且可以在其中处理数据或信息。例如:

复制代码

@code{

private void HandleCounterChanged(int counter)

{

Console.WriteLine($"The counter value is {counter}");

}

}

复制代码

这样就实现了从子到父单向传递数据或信息,并且可以在任何时候触发。

使用组件参数和属性传递状态:适合父子组件之间的简单状态传递,可以使用[Parameter]或者级联参数[CascadingParameter]特性来标记组件参数,并且使用<Component Parameter="Value" />或者<CascadingValue Value="Value"><Component /></CascadingValue>语法来传递状态。

4、级联参数和值

复制代码

<!-- AppStateProvider.razor -->

<CascadingValue Value="this">

@ChildContent

</CascadingValue>

@code {

[Parameter]

public RenderFragment? ChildContent { get; set; }

private string theme = "light";

public string Theme

{

get => theme;

set

{

if (theme != value)

{

theme = value;

StateHasChanged();

}

}

}

public event Action? OnThemeChanged;

public void ToggleTheme()

{

Theme = Theme == "light" ? "dark" : "light";

OnThemeChanged?.Invoke();

}

}

复制代码

复制代码

<!-- ConsumerComponent.razor -->

<div class="@($"app-{appState.Theme}")">

<h3>当前主题: @appState.Theme</h3>

<button @onclick="appState.ToggleTheme">切换主题</button>

</div>

@code {

[CascadingParameter]

public AppStateProvider appState { get; set; } = default!;

protected override void OnInitialized()

{

if (appState != null)

{

appState.OnThemeChanged += StateHasChanged;

}

}

public void Dispose()

{

if (appState != null)

{

appState.OnThemeChanged -= StateHasChanged;

}

}

}

复制代码

5、状态容器模式(全局状态)

创建状态容器服务

复制代码

// Services/AppState.cs

public class AppState

{

private int _counter;

private string _userName = string.Empty;

public int Counter

{

get => _counter;

set

{

_counter = value;

OnCounterChanged?.Invoke();

}

}

public string UserName

{

get => _userName;

set

{

_userName = value;

OnUserNameChanged?.Invoke();

}

}

public event Action? OnCounterChanged;

public event Action? OnUserNameChanged;

public void IncrementCounter()

{

Counter++;

}

}

复制代码

注册服务

// Program.cs

builder.Services.AddScoped<AppState>();

在组件中使用

复制代码

@inject AppState AppState

@implements IDisposable

<h3>计数器: @AppState.Counter</h3>

<h4>用户: @AppState.UserName</h4>

<button @onclick="AppState.IncrementCounter">增加计数</button>

<input @bind="localUserName" @bind:event="onchange" placeholder="输入用户名" />

@code {

private string localUserName

{

get => AppState.UserName;

set

{

AppState.UserName = value;

// 可以在这里添加其他逻辑

}

}

protected override void OnInitialized()

{

AppState.OnCounterChanged += StateHasChanged;

AppState.OnUserNameChanged += StateHasChanged;

}

public void Dispose()

{

AppState.OnCounterChanged -= StateHasChanged;

AppState.OnUserNameChanged -= StateHasChanged;

}

}

复制代码

6、Flux/Redux 模式

什么是Flux模式?

Flux是一种应用程序架构模式,专门用于管理前端应用中的状态。与常见的MVC模式不同,Flux采用单向数据流的设计,使得状态变化更加可预测和易于追踪。

Flux模式的核心思想是将状态管理与UI渲染分离,通过严格的规则来规范状态变更的过程。这种模式最初由Facebook提出,后来被Redux等库实现,而Fluxor则是专门为Blazor应用设计的实现方案。

Flux模式的核心原则

状态只读原则

应用的状态在任何情况下都不应该被直接修改,这保证了状态变更的可控性。

动作驱动变更

任何状态变更都必须通过派发(dispatch)一个动作(action)来触发。动作是一个简单的对象,描述了发生了什么变化。

纯函数处理

使用称为"reducer"的纯函数来处理动作,根据当前状态和动作生成新状态。Reducer不会修改原有状态,而是返回全新的状态对象。

单向数据流

UI组件订阅状态变化,当状态更新时自动重新渲染。用户交互则通过派发动作来触发状态变更,形成完整的单向循环。

核心概念

‌状态(State)‌:定义应用数据模型,不可直接修改,需通过动作(Action)触发更新。

‌动作(Action)‌:描述状态变更意图的对象,包含类型标识和有效载荷。

‌归约器(Reducer)‌:纯函数,根据当前状态和动作生成新状态。

‌效果(Effect)‌:处理副作用操作(如 API 调用),监听动作并执行异步任务。

中间件(Middleware):中间件可以在动作被派发到reducer之前或之后执行自定义逻辑,用于日志记录、性能监控等横切关注点。

使用 Fluxor 库

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/31 19:57:57

研究生必备7款免费AI论文神器:一键极速生成超长篇幅论文

如果你是正在熬夜赶Deadline的毕业生… 凌晨两点&#xff0c;电脑屏幕映出你布满血丝的眼睛&#xff1b;导师的微信语音里那句“论文进度怎么样了&#xff1f;”像一记重锤敲在心上&#xff1b;知网查重一次要上百块&#xff0c;钱包空空却不敢停笔&#xff1b;眼看延毕的阴影…

作者头像 李华
网站建设 2026/4/1 6:57:56

MySQL中root用户密码管理

前言 记录一下mysql中root用户密码的管理方式&#xff0c;mysql中root用户密码管理方式主要分为三个场景&#xff1a; 场景一&#xff1a;首次部署mysql&#xff0c;需要设置root用户密码 场景二&#xff1a;已知mysql的root用户密码&#xff0c;但是需要修改对应的密码&…

作者头像 李华
网站建设 2026/3/27 8:53:57

真正影响孩子视力的元凶曝光,不是手机,而是这个日常习惯!

在电子产品普及的当下&#xff0c;“控制孩子玩手机、看电视时间”几乎成为每个家长守护孩子视力的核心准则。当孩子出现揉眼、看远处模糊等症状时&#xff0c;家长们的第一反应往往是“电子产品玩多了”。然而&#xff0c;越来越多的研究数据表明&#xff0c;真正威胁儿童视力…

作者头像 李华
网站建设 2026/3/30 9:56:26

【URP】Unity[置换贴图]原理与实践

技术原理与解决的问题‌功能差异‌&#xff1a;置换贴图通过灰度值控制顶点位移&#xff08;白色凸起&#xff0c;黑色凹陷&#xff09;&#xff0c;相比法线贴图能产生真实的轮廓阴影和遮挡效果&#xff0c;解决了低模表现高精度几何细节的难题。‌性能权衡‌&#xff1a;需要…

作者头像 李华
网站建设 2026/4/1 6:53:29

Java毕设选题推荐:基于springboot的人力资源管理系统的设计与实现员工管理,部门管理,员工考勤管理,请假申请管理【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/3/16 6:11:21

2025年 AI 智能体企业级落地现状报告

LangChain调查了 1300 多名专业人士&#xff0c;涵盖工程师、产品经理、业务负责人及企业高管&#xff0c;旨在揭示 AI 智能体的发展现状。让我们深入解读数据&#xff0c;剖析如今 AI 智能体的应用&#xff08;或未被应用&#xff09;情况。 1. 引言 步入 2026 年&#xff0…

作者头像 李华