<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      ASP.NET Core Blazor 核心功能一:Blazor依賴注入與狀態管理指南

      大家好,我是碼農剛子本文詳細介紹了Blazor框架中的依賴注入機制和狀態管理方案。依賴注入部分闡述了服務注冊的三種生命周期方式(Singleton/Scoped/Transient)及在組件中的使用方法。狀態管理章節系統梳理了7種解決方案:從簡單的組件內狀態到父子組件通信、級聯參數,再到全局狀態容器和Flux/Redux模式,并提供了本地存儲持久化方案。文章還介紹了@ref指令的使用場景,包括組件引用、元素操作和循環處理等。最后給出了不同場景下的狀態管理選擇建議,幫助開發者構建更健壯。

      一、依賴注入基礎

      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模式的核心原則

      1. 狀態只讀原則

      應用的狀態在任何情況下都不應該被直接修改,這保證了狀態變更的可控性。

      1. 動作驅動變更

      任何狀態變更都必須通過派發(dispatch)一個動作(action)來觸發。動作是一個簡單的對象,描述了發生了什么變化。

      1. 純函數處理

      使用稱為"reducer"的純函數來處理動作,根據當前狀態和動作生成新狀態。Reducer不會修改原有狀態,而是返回全新的狀態對象。

      1. 單向數據流

      UI組件訂閱狀態變化,當狀態更新時自動重新渲染。用戶交互則通過派發動作來觸發狀態變更,形成完整的單向循環。

      核心概念

      • ?狀態(State)?:定義應用數據模型,不可直接修改,需通過動作(Action)觸發更新。
      • ?動作(Action)?:描述狀態變更意圖的對象,包含類型標識和有效載荷。
      • ?歸約器(Reducer)?:純函數,根據當前狀態和動作生成新狀態。
      • ?效果(Effect)?:處理副作用操作(如 API 調用),監聽動作并執行異步任務。
      • 中間件(Middleware)中間件可以在動作被派發到reducer之前或之后執行自定義邏輯,用于日志記錄、性能監控等橫切關注點。

      使用 Fluxor 庫

      首先安裝 Fluxor:

      Install-Package Fluxor.Blazor.Web

      定義狀態和動作

      // Store/CounterState.cs
      public record CounterState
      {
          public int Count { get; init; }
      }
       
      // Store/Actions/IncrementCounterAction.cs
      public record IncrementCounterAction;
       
      // Store/Reducers/CounterReducers.cs
      public static class CounterReducers
      {
          [ReducerMethod]
          public static CounterState OnIncrementCounter(CounterState state, IncrementCounterAction action)
          {
              return state with { Count = state.Count + 1 };
          }
      }

      在組件中使用

      @using Fluxor
      @inherits Fluxor.Blazor.Web.Components.FluxorComponent
       
      <h3>計數器: @State.Value.Count</h3>
      <button @onclick="Increment">增加</button>
       
      @code {
          [Inject]
          private IState<CounterState> State { get; set; } = null!;
       
          [Inject]
          private IDispatcher Dispatcher { get; set; } = null!;
       
          private void Increment()
          {
              Dispatcher.Dispatch(new IncrementCounterAction());
          }
      }

      7、本地存儲持久化

      使用 Blazor 本地存儲

      @page "/counter2"
      @inject IJSRuntime JSRuntime
       
      <h3>持久化計數器: @count</h3>
      <button @onclick="Increment">增加并保存</button>
       
      @code {
          private int count = 0;
          private bool isInitialized = false;
       
          protected override async Task OnAfterRenderAsync(bool firstRender)
          {
              if (firstRender)
              {
                  await LoadFromStorage();
                  isInitialized = true;
                  StateHasChanged(); // 確保在加載后更新UI
              }
          }
       
          private async Task Increment()
          {
              count++;
              await SaveToStorage();
              //StateHasChanged();
          }
       
          private async Task SaveToStorage()
          {
              if (isInitialized)
              {
                  await JSRuntime.InvokeVoidAsync("localStorage.setItem", "counter", count);
              }
          }
       
          private async Task LoadFromStorage()
          {
              try
              {
                  var savedCount = await JSRuntime.InvokeAsync<string>("localStorage.getItem", "counter");
                  if (int.TryParse(savedCount, out int result))
                  {
                      count = result;
                  }
              }
              catch (Exception ex)
              {
                  // 處理預渲染期間的 JS 互操作錯誤
                  Console.WriteLine($"加載存儲時出錯: {ex.Message}");
              }
          }
       
      }

      8、狀態管理選擇指南

      image

      四、使用 @ref 引用組件

      在 Blazor 中,@ref 指令用于獲取對組件或 HTML 元素的引用,讓你能夠在代碼中直接操作它們。以下是詳細的使用方法:

      1、引用組件

      基本用法

      <!-- MyComponent.razor -->
      <h3>計數器: @count</h3>
      <button @onclick="Increment">增加</button>
       
      @code {
          private int count = 0;
       
          public void Increment()
          {
              count++;
              StateHasChanged();
          }
       
          public void Reset()
          {
              count = 0;
              StateHasChanged();
          }
      }
      <!-- ParentComponent.razor -->
      @page "/parent"
       
      <MyComponent @ref="myComponentRef" />
      <button @onclick="ResetChild">重置子組件</button>
       
      @code {
          private MyComponent? myComponentRef;
       
          private void ResetChild()
          {
              myComponentRef?.Reset();
          }
      }

      2、引用 HTML 元素

      @page "/element-ref"
       
      <input @ref="usernameInput" placeholder="輸入用戶名" />
      <button @onclick="FocusInput">聚焦輸入框</button>
       
      @code {
          private ElementReference usernameInput;
       
          private async Task FocusInput()
          {
              await usernameInput.FocusAsync();
          }
      }

      3、在循環中使用 @ref

      @page "/loop-ref-example"
       
      <h3>循環中使用 ref 示例</h3>
       
      <button @onclick="ShowAllMessages" class="btn btn-primary">顯示所有消息</button>
      <button @onclick="UpdateAllMessages" class="btn btn-secondary">更新所有消息</button>
       
      @foreach (var item in items)
      {
          <ChildComponent @ref="componentRefs[item.Id]"
                          Id="item.Id"
                          Message="@item.Message"
                          OnMessageChanged="HandleMessageChanged" />
      }
       
      @code {
          private List<ItemModel> items = new();
          private Dictionary<int, ChildComponent?> componentRefs = new();
       
          protected override void OnInitialized()
          {
              items = new List<ItemModel>
              {
                  new ItemModel { Id = 1, Message = "第一條消息" },
                  new ItemModel { Id = 2, Message = "第二條消息" },
                  new ItemModel { Id = 3, Message = "第三條消息" }
              };
              // 預先初始化字典
              foreach (var item in items)
              {
                  componentRefs[item.Id] = null;
              }
          }
       
          private void ShowAllMessages()
          {
              foreach (var component in componentRefs.Values)
              {
                  component?.ShowCurrentMessage();
              }
          }
       
          private void UpdateAllMessages()
          {
              foreach (var item in items)
              {
                  if (componentRefs.TryGetValue(item.Id, out var component) && component != null)
                  {
                      component.UpdateMessage($"更新后的消息 {item.Id}");
                  }
              }
          }
       
          private void HandleMessageChanged((int Id, string Message) data)
          {
              Console.WriteLine($"收到消息更新 - ID: {data.Id}, 消息: {data.Message}");
       
              var item = items.FirstOrDefault(i => i.Id == data.Id);
              if (item != null)
              {
                  item.Message = data.Message;
                  StateHasChanged();
              }
          }
       
          public class ItemModel
          {
              public int Id { get; set; }
              public string Message { get; set; } = string.Empty;
          }
      }
      <!-- ChildComponent.razor -->
      <div class="child-component">
          <h5>子組件 ID: @Id</h5>
          <p>當前消息: <strong>@Message</strong></p>
          <input @bind="currentMessage" @bind:event="oninput" />
          <button @onclick="UpdateMessage" class="btn btn-sm btn-info">更新消息</button>
      </div>
       
      @code {
          [Parameter]
          public int Id { get; set; }
       
          [Parameter]
          public string Message { get; set; } = string.Empty;
       
          [Parameter]
          public EventCallback<(int Id, string Message)> OnMessageChanged { get; set; }
       
          private string currentMessage = string.Empty;
       
          protected override void OnParametersSet()
          {
              currentMessage = Message;
          }
       
          private async Task UpdateMessage()
          {
              await OnMessageChanged.InvokeAsync((Id, currentMessage));
          }
       
          public void ShowCurrentMessage()
          {
              Console.WriteLine($"組件 {Id} 的消息: {Message}");
          }
       
          public void UpdateMessage(string newMessage)
          {
              currentMessage = newMessage;
              UpdateMessage().Wait();
          }
      }

      4、使用 ref 回調

      <CustomInput @ref="SetInputRef" />
       
      @code {
          private CustomInput? inputRef;
       
          private void SetInputRef(CustomInput component)
          {
              inputRef = component;
              // 組件引用設置后的初始化邏輯
              component?.Initialize();
          }
      }

      5、與 JavaScript 互操作

      @inject IJSRuntime JSRuntime
       
      <div @ref="myDiv" style="width: 100px; height: 100px; background: red;"></div>
      <button @onclick="ChangeDivStyle">修改樣式</button>
       
      @code {
          private ElementReference myDiv;
       
          private async Task ChangeDivStyle()
          {
              await JSRuntime.InvokeVoidAsync("changeElementStyle", myDiv);
          }
      }

      對應的 JavaScript 文件:

      // wwwroot/js/site.js
      window.changeElementStyle = (element) => {
          element.style.background = 'blue';
          element.style.width = '200px';
      };

      本章節中用到:IJSRuntime ,后面會詳細講解。

      以上就是關于《ASP.NET Core Blazor 核心功能一:Blazor依賴注入與狀態管理指南》的全部內容,希望你有所收獲。關注我,持續分享。

      posted @ 2025-10-29 10:07  碼農剛子  閱讀(222)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品亚洲аv无码播放| 极品粉嫩小泬无遮挡20p| 超碰成人精品一区二区三| 偷拍专区一区二区三区| 亚洲电影在线观看| 久久久久蜜桃精品成人片公司| 丰满少妇被猛烈进出69影院| 无码人妻一区二区三区av| 亚洲色偷偷色噜噜狠狠99| 国产不卡一区二区在线视频| 国产成人无码A区在线观看视频| 欧美牲交a欧美牲交aⅴ免费| 高清破外女出血AV毛片| 扒开双腿猛进入喷水高潮叫声| 无套内谢少妇毛片aaaa片免费| 成人片在线看无码不卡| 欧美人妻在线一区二区| 成人无码区在线观看| 加勒比亚洲视频在线播放| 亚洲 日本 欧洲 欧美 视频| 偷拍一区二区三区在线视频| 亚洲国产精品人人做人人爱| 成人综合婷婷国产精品久久蜜臀 | 国产午夜成人久久无码一区二区| 国产精品香蕉在线观看不卡| 免费国产一级 片内射老| 亚洲av无码成人精品区一区| 国产成人午夜福利精品| 国产精品会所一区二区三区| 国产精品欧美福利久久| 国产精品久久久福利| 亚洲av网一区天堂福利| 91久久国产成人免费观看| 亚洲综合精品第一页| 好爽毛片一区二区三区四| 国产成人亚洲一区二区三区| 中文字幕乱码无码人妻系列蜜桃| 激情综合网激情五月激情 | 蜜桃一区二区三区在线看| 亚洲综合激情五月色一区| 不卡av电影在线|