一致性Hash算法(KetamaHash)的c#实现

颐矩位拾大家好,我是码农刚子。上一章介绍了Blazor的简介,开发工具及环境,基本语法和一些示例。接下来我们继续了解Blazor 组件相关的基础知识,希望对你有所帮助。

1、组件生命周期

1.简介

Blazor的生命周期与React组件的生命周期类似,也分为三个阶段:初始化阶段、运行中阶段和销毁阶段,其相关方法有10个,包括设置参数前、初始化、设置参数之后、组件渲染后以及组件的销毁,但是这些方法有些是重复的,只不过是同步与异步的区别。

2.图解

首先将结果图呈现,代码位于第3部分:

Blazor生命周期方法主要包括:

1

设置参数前

SetParametersAsync

2

初始化

OnInitialized/OnInitializedAsync

3

设置参数后

OnParametersSet/OnParametersSetAsync

4

组件渲染呈现后

OnAfterRender/OnAfterRenderAsync

5

判断是否渲染组件

ShouldRender

6

组件删除前

Dispose

7

通知组件渲染

StateHasChanged

在所有生命周期函数中,有以下需要注意的点:

(1)前5种方法的声明都是virtual,除SetParametersAsync为public外,其他的都是protected。

(2)OnAfterRender/OnAfterRenderAsync方法有一个bool类型的形参firstRender,用于指示是否是第一次渲染(即组件初始化时的渲染)。

(3)同步方法总是先于异步方法执行。

(4)Dispose函数需要通过使用@implements指令实现IDisposable接口来实现。

(5)StateHasChanged无法被重写,可以被显示调用,以便强制实现组件刷新(如果ShouldRender返回true,并且Blazor认为需要刷新);当组件状态更改时不必显示调用此函数,也可导致组件的重新渲染(如果ShouldRender返回true),因为其已经在ComponentBase内部的处理过程(第一次初始化设置参数时、设置参数后和DOM事件处理等)中被调用。

3.代码示例

设置参数时 (SetParametersAsync 设置由组件的父组件在呈现树或路由参数中提供的参数。

每次调用 ParameterView 时,方法的 参数都包含该组件的SetParametersAsync值集。 通过重写 SetParametersAsync 方法,C#代码可以直接与 ParameterView 参数交互。

@page "/set-params-async/{Param?}"

Set Parameters Async

Set Parameters Async Example

@message

@code {

private string message = "Not set";

Parameter

public string? Param { get; set; }

public override async Task SetParametersAsync(ParameterView parameters)

{

if (parameters.TryGetValue(nameof(Param), out var value))

{

if (value is null)

{

message = "The value of 'Param' is null.";

}

else

{

message = $"The value of 'Param' is {value}.";

}

}

await base.SetParametersAsync(parameters);

}

}

组件初始化 (OnInitialized 和 OnInitializedAsync 专门用于在组件实例的整个生命周期内初始化组件。 参数值和参数值更改不应影响在这些方法中执行的初始化。 例如,将静态选项加载到下拉列表中,该下拉列表在组件的生命周期内不会更改,也不依赖于参数值,这是在这些生命周期方法之一中执行的操作。 如果参数值或参数值更改会影响组件状态,请改为使用 OnParametersSet{Async}。

组件在接收 SetParametersAsync 中的初始参数后初始化,此时,将调用这些方法。

如果使用同步父组件初始化,则保证父组件初始化在子组件初始化之前完成。 如果使用异步父组件初始化,则无法确定父组件和子组件初始化的完成顺序,因为它取决于正在运行的初始化代码。

对于同步操作,重写 OnInitialized:

@page "/on-init"

On Initialized

On Initialized Example

@message

@code {

private string? message;

protected override void OnInitialized() =>

message = $"Initialized at {DateTime.Now}";

}

若要执行异步操作,请替代 OnInitializedAsync 并使用 await 运算符:

protected override async Task OnInitializedAsync()

{

//await ...

await Task.Delay(2000); //2秒之后

message = $"Initialized at {DateTime.Now} after 2 second delay";

}

如果自定义基类与自定义初始化逻辑一起使用,需在基类上调用 OnInitializedAsync:

protected override async Task OnInitializedAsync()

{

await ...

await base.OnInitializedAsync();

}

设置参数之后 (OnParametersSet 或 OnParametersSetAsync 在以下情况下调用:

在 OnInitialized 或 OnInitializedAsync 中初始化组件后。

当父组件重新呈现并提供以下内容时:

至少一个参数已更改时的已知或基元不可变类型。

复杂类型的参数。 框架无法知道复杂类型参数的值是否在内部发生了改变,因此,如果存在一个或多个复杂类型的参数,框架始终将参数集视为已更改。

在组件路由中,不能同时对DateTime参数使用datetime路由约束,并将该参数设为可选。 因此,以下 OnParamsSet 组件使用两个 @page 指令来处理具有和没有 URL 中提供的日期段的路由。

@page "/on-params-set"

@page "/on-params-set/{StartDate:datetime}"

On Parameters Set

On Parameters Set Example

Pass a datetime in the URI of the browser's address bar.

For example, add /1-1-2024 to the address.

@message

@code {

private string? message;

Parameter

public DateTime StartDate { get; set; }

protected override void OnParametersSet()

{

if (StartDate == default)

{

StartDate = DateTime.Now;

message = $"No start date in URL. Default value applied " +

$"(StartDate: {StartDate}).";

}

else

{

message = $"The start date in the URL was used " +

$"(StartDate: {StartDate}).";

}

}

}

应用参数和属性值时,异步操作必须在 OnParametersSetAsync 生命周期事件期间发生:

protected override async Task OnParametersSetAsync()

{

await ...

}

如果自定义基类与自定义初始化逻辑一起使用,需在基类上调用 OnParametersSetAsync:

protected override async Task OnParametersSetAsync()

{

await ...

await base.OnParametersSetAsync();

}

组件呈现之后 (OnAfterRender 和 OnAfterRenderAsync 在组件以交互方式呈现并且 UI 完成更新之后被调用(例如,元素添加到浏览器 DOM 之后)。 此时会填充元素和组件引用。 在此阶段中,可使用呈现的内容执行其他初始化步骤,例如与呈现的 DOM 元素交互的 JS 互操作调用。

这些方法不会在预呈现或静态服务器端渲染(静态 SSR)期间在服务器上调用,因为这些进程未附加到实时浏览器 DOM,并且已在 DOM 更新之前完成。

对于 OnAfterRenderAsync,组件在任何返回 Task 的操作完成后不会自动重渲染,以避免无限渲染循环。

firstRender 和 OnAfterRender 的 OnAfterRenderAsync 参数:

在第一次呈现组件实例时设置为 true。

可用于确保初始化操作仅执行一次。

@page "/after-render"

@inject ILogger Logger

After Render

After Render Example

Log information (and trigger a render)

Study logged messages in the console.

@code {

protected override void OnAfterRender(bool firstRender) =>

Logger.LogInformation("firstRender = {FirstRender}", firstRender);

private void HandleClick() => Logger.LogInformation("HandleClick called");

}

加载页面并选择按钮时,AfterRender.razor 示例向控制台输出以下内容:

在渲染后立即进行的异步工作必须在 OnAfterRenderAsync 生命周期事件期间发生:

protected override async Task OnAfterRenderAsync(bool firstRender)

{

...

}

如果自定义基类与自定义初始化逻辑一起使用,需在基类上调用 OnAfterRenderAsync:

protected override async Task OnAfterRenderAsync(bool firstRender)

{

...

await base.OnAfterRenderAsync(firstRender);

}

基类生命周期方法

重写 Blazor 的生命周期方法时,无需为 ComponentBase 调用基类生命周期方法。 但在以下情况下,组件应调用重写的基类生命周期方法:

重写 ComponentBase.SetParametersAsync 时,通常会调用 await base.SetParametersAsync(parameters);, 因为基类方法会调用其他生命周期方法并以复杂的方式触发渲染。 有关详细信息,请参阅设置参数时 (SetParametersAsync) 部分。

如果基类方法包含必须执行的逻辑。 库使用者通常在继承基类时调用基类生命周期方法,因为库基类通常具有要执行的自定义生命周期逻辑。 如果应用使用某个库中的基类,请参阅该库的文档以获取指导。

以下示例中调用了 base.OnInitialized(); 以确保会执行基类的 OnInitialized 方法。 如果没有调用,BlazorRocksBase2.OnInitialized 不会执行。

@page "/blazor-rocks-2"

@inherits BlazorRocksBase2

@inject ILogger Logger

Blazor Rocks!

Blazor Rocks! Example 2

@BlazorRocksText

@code {

protected override void OnInitialized()

{

Logger.LogInformation("Initialization code of BlazorRocks2 executed!");

base.OnInitialized();

}

}

using Microsoft.AspNetCore.Components;

namespace BlazorAppWasm

{

public class BlazorRocksBase2: ComponentBase

{

Inject

private ILogger Logger { get; set; } = default!;

public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";

protected override void OnInitialized() =>

Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");

}

}

2、数据绑定

Blazor提供了强大的数据绑定机制,主要包括单向绑定和双向绑定两种模式。

  1. 单向数据绑定

单向绑定是指数据从组件流向UI,但UI的变化不会自动更新数据源。

基本语法

当前值: @currentValue

用户名: @UserName

创建时间: @CreateTime.ToString("yyyy-MM-dd")

完整示例

单向绑定示例

计数器: @count

消息: @message

用户信息: @user.Name - @user.Age

增加计数

更改消息

更新用户

@code {

private int count = 0;

private string message = "初始消息";

private User user = new User { Name = "张三", Age = 25 };

private void Increment()

{

count++;

// StateHasChanged(); // 通常不需要手动调用,事件处理会自动触发重新渲染

}

private void ChangeMessage()

{

message = $"消息已更新: {DateTime.Now:HH:mm:ss}";

}

private void UpdateUser()

{

user = new User { Name = "李四", Age = 30 };

}

class User

{

public string Name { get; set; } = string.Empty;

public int Age { get; set; }

}

}

  1. 双向数据绑定

双向绑定允许数据在组件和UI之间双向流动:UI变化自动更新数据源,数据源变化自动更新UI。

基本语法

  • ...
  • 完整示例

双向绑定示例

用户名:

显示: @userName

邮箱:

显示: @email

年龄:

显示: @age

城市:

请选择

北京

上海

广州

深圳

选择: @selectedCity

是否同意协议:

@(isAgreed ? "已同意" : "未同意")

汇总信息:

用户名: @userName

邮箱: @email

年龄: @age

城市: @selectedCity

同意协议: @isAgreed

  • @code {
  • private string userName = string.Empty;
  • private string email = string.Empty;
  • private int age = 0;
  • private string selectedCity = string.Empty;
  • private bool isAgreed = false;
  • }
    1. 绑定事件控制
  • 3.1 绑定特定事件
  • 默认情况下,@bind 在失去焦点时更新。可以使用 @bind:event 指定触发事件:
实时绑定示例

placeholder="输入搜索内容..." />

实时搜索: @searchText

默认绑定: @normalText

  • @code {
  • private string searchText = string.Empty;
  • private string normalText = string.Empty;
  • }
  • 3.2 绑定格式化
格式化绑定示例

选择的日期: @startDate.ToString("yyyy年MM月dd日")

价格: @price.ToString("C")

  • @code {
  • private DateTime startDate = DateTime.Today;
  • private decimal price = 0.00m;
  • }
    1. 自定义组件双向绑定
  • 在自定义组件中实现双向绑定:
  • 子组件
    @Label

value="@Value"

@οninput="HandleInput"

class="form-control @AdditionalClass"

placeholder="@Placeholder" />

@if (!string.IsNullOrEmpty(ValidationMessage))

{

@ValidationMessage

}

  • @code {
  • Parameter

  • public string Value { get; set; } = string.Empty;
  • Parameter

  • public EventCallback ValueChanged { get; set; }
  • Parameter

  • public string Label { get; set; } = string.Empty;
  • Parameter

  • public string Placeholder { get; set; } = string.Empty;
  • Parameter

  • public string AdditionalClass { get; set; } = string.Empty;
  • Parameter

  • public string ValidationMessage { get; set; } = string.Empty;
  • private async Task HandleInput(ChangeEventArgs e)
  • {
  • Value = e.Value?.ToString() ?? string.Empty;
  • await ValueChanged.InvokeAsync(Value);
  • }
  • }
  • 父组件使用

自定义组件双向绑定

@bind-Value="userName"

Label="用户名"

Placeholder="请输入用户名" />

@bind-Value="email"

Label="邮箱"

Placeholder="请输入邮箱地址"

ValidationMessage="@(IsValidEmail ? "" : "邮箱格式不正确")" />

用户名: @userName

邮箱: @email

  • @code {
  • private string userName = string.Empty;
  • private string email = string.Empty;
  • private bool IsValidEmail => email.Contains("@") && email.Contains(".");
  • }
  • 5.复杂对象绑定

复杂对象绑定

用户信息

姓名:

年龄:

地址:

当前用户信息:
复制代码
@userInfoJson

重置用户

创建新用户

  • @code {
  • private User currentUser = new User();
  • private string userInfoJson =>
  • System.Text.Json.JsonSerializer.Serialize(currentUser, new System.Text.Json.JsonSerializerOptions
  • {
  • WriteIndented = true
  • });
  • private void ResetUser()
  • {
  • currentUser = new User();
  • }
  • private void CreateNewUser()
  • {
  • currentUser = new User
  • {
  • Name = "新用户",
  • Age = 18,
  • Address = new Address { Street = "新建街道", City = "新建城市" }
  • };
  • }
  • class User
  • {
  • public string Name { get; set; } = string.Empty;
  • public int Age { get; set; }
  • public Address Address { get; set; } = new Address();
  • }
  • class Address
  • {
  • public string Street { get; set; } = string.Empty;
  • public string City { get; set; } = string.Empty;
  • }
  • }
  • 6.绑定模式对比
  • 绑定类型
  • 语法
  • 更新时机
  • 适用场景
  • 单向绑定
  • @property
  • 数据源变化时
  • 显示数据、计算属性
  • 双向绑定
  • @bind="property"
  • 失去焦点时
  • 表单输入、用户交互
  • 实时双向
  • @bind="property" @bind:event="oninput"
  • 输入时实时更新
  • 搜索框、实时验证
  • 自定义绑定
  • @bind-Value="property"
  • 自定义事件触发
  • 自定义表单组件
  • 3、事件处理
    1. 基本事件处理
  • 1.1 单击事件

单击事件示例

点击我

按钮 1

按钮 2

按钮 3

最后点击的按钮: @lastClickedButton

点击次数: @clickCount

  • @code {
  • private int lastClickedButton = 0;
  • private int clickCount = 0;
  • private void HandleClick()
  • {
  • clickCount++;
  • Console.WriteLine("按钮被点击了!");
  • }
  • private void HandleButtonClick(int buttonNumber)
  • {
  • lastClickedButton = buttonNumber;
  • clickCount++;
  • StateHasChanged();
  • }
  • }
  • 1.2 异步事件处理

异步事件处理

@if (isLoading)

{

加载中...

}

else

{

模拟异步操作

}

操作结果: @operationResult

耗时: @elapsedTime 毫秒

  • @code {
  • private bool isLoading = false;
  • private string operationResult = string.Empty;
  • private long elapsedTime = 0;
  • private async Task HandleAsyncClick()
  • {
  • isLoading = true;
  • operationResult = "操作开始...";
  • var stopwatch = System.Diagnostics.Stopwatch.StartNew();
  • // 模拟异步操作
  • await Task.Delay(2000);
  • stopwatch.Stop();
  • elapsedTime = stopwatch.ElapsedMilliseconds;
  • operationResult = $"操作完成!数据已保存。";
  • isLoading = false;
  • StateHasChanged();
  • }
  • }
    1. 表单事件处理
  • 2.1 输入事件

表单事件处理

输入文本:

@οnchange="HandleChange"

class="form-control"

placeholder="输入内容..." />

实时输入: @inputValue | 变化事件: @changeValue

选择选项:

请选择

选项一

选项二

选项三

选择的值: @selectedValue

同意条款

状态: @(isChecked ? "已选中" : "未选中")

用户名:

邮箱:

提交表单

表单数据:
复制代码
@System.Text.Json.JsonSerializer.Serialize(user, new System.Text.Json.JsonSerializerOptions { WriteIndented = true })

提交状态: @submitStatus

  • @code {
  • private string inputValue = string.Empty;
  • private string changeValue = string.Empty;
  • private string selectedValue = string.Empty;
  • private bool isChecked = false;
  • private string submitStatus = "未提交";
  • private User user = new User();
  • private void HandleInput(ChangeEventArgs e)
  • {
  • inputValue = e.Value?.ToString() ?? string.Empty;
  • }
  • private void HandleChange(ChangeEventArgs e)
  • {
  • changeValue = e.Value?.ToString() ?? string.Empty;
  • }
  • private void HandleSelectChange(ChangeEventArgs e)
  • {
  • selectedValue = e.Value?.ToString() ?? string.Empty;
  • }
  • private void HandleCheckboxChange(ChangeEventArgs e)
  • {
  • isChecked = (bool)(e.Value ?? false);
  • }
  • private void HandleSubmit()
  • {
  • submitStatus = "表单提交(可能有验证错误)";
  • }
  • private void HandleValidSubmit()
  • {
  • submitStatus = $"表单验证通过!数据已保存 - {DateTime.Now:HH:mm:ss}";
  • // 这里可以调用API保存数据
  • }
  • class User
  • {
  • public string Username { get; set; } = string.Empty;
  • public string Email { get; set; } = string.Empty;
  • }
  • }
    1. 鼠标和键盘事件
  • 3.1 鼠标事件

鼠标事件

@οnmοusedοwn="HandleMouseDown"

@οnmοuseup="HandleMouseUp"

@οnmοusemοve="HandleMouseMove"

@οnmοuseοver="HandleMouseOver"

@οnmοuseοut="HandleMouseOut"

@οnclick="HandleAreaClick"

@οndblclick="HandleDoubleClick"

style="width: 300px; height: 200px; border: 2px solid #007bff; padding: 20px; margin: 10px 0;">

鼠标交互区域

事件日志:
  • @foreach (var log in eventLogs.TakeLast(10).Reverse())

    {

    • @log

    }

鼠标位置: (@mouseX, @mouseY)

按钮状态: @(isMouseDown ? "按下" : "释放")

悬停状态: @(isMouseOver ? "在区域内" : "在区域外")

  • @code {
  • private double mouseX = 0;
  • private double mouseY = 0;
  • private bool isMouseDown = false;
  • private bool isMouseOver = false;
  • private List eventLogs = new List();
  • private void LogEvent(string eventName)
  • {
  • eventLogs.Add($"{DateTime.Now:HH:mm:ss.fff} - {eventName}");
  • StateHasChanged();
  • }
  • private void HandleMouseDown(MouseEventArgs e)
  • {
  • isMouseDown = true;
  • LogEvent($"MouseDown - 按钮: {e.Button}, 位置: ({e.ClientX}, {e.ClientY})");
  • }
  • private void HandleMouseUp(MouseEventArgs e)
  • {
  • isMouseDown = false;
  • LogEvent($"MouseUp - 按钮: {e.Button}, 位置: ({e.ClientX}, {e.ClientY})");
  • }
  • private void HandleMouseMove(MouseEventArgs e)
  • {
  • mouseX = e.ClientX;
  • mouseY = e.ClientY;
  • // 注意:频繁触发,生产环境需要节流
  • // LogEvent($"MouseMove - 位置: ({e.ClientX}, {e.ClientY})");
  • }
  • private void HandleMouseOver(MouseEventArgs e)
  • {
  • isMouseOver = true;
  • LogEvent("MouseOver");
  • }
  • private void HandleMouseOut(MouseEventArgs e)
  • {
  • isMouseOver = false;
  • LogEvent("MouseOut");
  • }
  • private void HandleAreaClick(MouseEventArgs e)
  • {
  • LogEvent($"Click - 按钮: {e.Button}");
  • }
  • private void HandleDoubleClick(MouseEventArgs e)
  • {
  • LogEvent($"DoubleClick - 按钮: {e.Button}");
  • }
  • }
  • 3.2 键盘事件

键盘事件

@οnkeyup="HandleKeyUp"

@οnkeypress="HandleKeyPress"

class="form-control"

placeholder="在这里输入并观察键盘事件..." />

键盘事件日志:
  • @foreach (var log in keyEventLogs.TakeLast(10).Reverse())

    {

    • @log

    }

最后按下的键: @lastKey

Ctrl 按下: @(isCtrlPressed ? "是" : "否")

Shift 按下: @(isShiftPressed ? "是" : "否")

Alt 按下: @(isAltPressed ? "是" : "否")

  • @code {
  • private string lastKey = "无";
  • private bool isCtrlPressed = false;
  • private bool isShiftPressed = false;
  • private bool isAltPressed = false;
  • private List keyEventLogs = new List();
  • private void LogKeyEvent(string eventName, KeyboardEventArgs e)
  • {
  • var log = $"{DateTime.Now:HH:mm:ss.fff} - {eventName}: Key='{e.Key}', Code='{e.Code}'";
  • if (e.CtrlKey) log += " [Ctrl]";
  • if (e.ShiftKey) log += " [Shift]";
  • if (e.AltKey) log += " [Alt]";
  • keyEventLogs.Add(log);
  • StateHasChanged();
  • }
  • private void HandleKeyDown(KeyboardEventArgs e)
  • {
  • lastKey = e.Key;
  • isCtrlPressed = e.CtrlKey;
  • isShiftPressed = e.ShiftKey;
  • isAltPressed = e.AltKey;
  • LogKeyEvent("KeyDown", e);
  • // 快捷键处理示例
  • if (e.CtrlKey && e.Key == "s")
  • {
  • e.PreventDefault(); // 阻止浏览器默认保存行为
  • LogKeyEvent("快捷键: Ctrl+S", e);
  • }
  • }
  • private void HandleKeyUp(KeyboardEventArgs e)
  • {
  • isCtrlPressed = e.CtrlKey;
  • isShiftPressed = e.ShiftKey;
  • isAltPressed = e.AltKey;
  • LogKeyEvent("KeyUp", e);
  • }
  • private void HandleKeyPress(KeyboardEventArgs e)
  • {
  • LogKeyEvent("KeyPress", e);
  • }
  • }
    1. 焦点和剪贴板事件

焦点和剪贴板事件

焦点测试输入框:

@οnblur="HandleBlur"

class="form-control"

placeholder="点击获取焦点,点击别处失去焦点" />

复制粘贴测试:

<p> @oncut="HandleCut"</p> <p> @οnpaste="HandlePaste"</p> <p> class="form-control"</p> <p> rows="3"</p> <p> placeholder="在这里测试复制、剪切、粘贴操作">这是一些测试文本

事件状态:

焦点状态: @(hasFocus ? "有焦点" : "无焦点")

最后操作: @lastOperation

剪贴板内容: @clipboardContent

  • @code {
  • private bool hasFocus = false;
  • private string lastOperation = "无";
  • private string clipboardContent = "无";
  • private void HandleFocus(FocusEventArgs e)
  • {
  • hasFocus = true;
  • lastOperation = "获得焦点";
  • StateHasChanged();
  • }
  • private void HandleBlur(FocusEventArgs e)
  • {
  • hasFocus = false;
  • lastOperation = "失去焦点";
  • StateHasChanged();
  • }
  • private void HandleCopy(ClipboardEventArgs e)
  • {
  • lastOperation = "复制操作";
  • clipboardContent = "复制的内容无法直接获取(安全限制)";
  • StateHasChanged();
  • }
  • private void HandleCut(ClipboardEventArgs e)
  • {
  • lastOperation = "剪切操作";
  • clipboardContent = "剪切的内容无法直接获取(安全限制)";
  • StateHasChanged();
  • }
  • private void HandlePaste(ClipboardEventArgs e)
  • {
  • lastOperation = "粘贴操作";
  • clipboardContent = "粘贴的内容无法直接获取(安全限制)";
  • StateHasChanged();
  • }
  • }
    1. 自定义事件处理
  • 5.1 事件参数封装

自定义事件处理

父级区域(点击会触发)

@onclick:stopPropagation

class="btn btn-primary">

子按钮(点击不会冒泡)

@onclick:preventDefault

class="btn btn-secondary">

阻止默认行为的按钮

自定义操作:

操作1

操作2

异步操作

操作日志:
  • @foreach (var log in actionLogs.TakeLast(5).Reverse())

    {

    • @log

    }

  • @code {

  • private List actionLogs = new List();

  • private void LogAction(string action)

  • {

  • actionLogs.Add($"{DateTime.Now:HH:mm:ss} - {action}");

  • StateHasChanged();

  • }

  • private void HandleParentClick()

  • {

  • LogAction("父级区域被点击");

  • }

  • private void HandleChildClick()

  • {

  • LogAction("子按钮被点击(事件不会冒泡)");

  • }

  • private void HandleChildClickWithPrevent()

  • {

  • LogAction("阻止默认行为的按钮被点击");

  • }

  • private void HandleCustomAction1()

  • {

  • LogAction("执行自定义操作1");

  • // 自定义业务逻辑

  • }

  • private void HandleCustomAction2(MouseEventArgs e)

  • {

  • LogAction($"执行自定义操作2 - 点击位置: ({e.ClientX}, {e.ClientY})");

  • // 自定义业务逻辑

  • }

  • private async Task HandleCustomAsyncAction()

  • {

  • LogAction("开始异步操作");

  • await Task.Delay(1000);

  • LogAction("异步操作完成");

  • }

  • }

    1. 事件处理最佳实践
  • 6.1 性能优化

事件处理性能优化

@foreach (var item in items)

{

@item.Name

删除

}

@foreach (var item in largeList)

{

@item.Name

}

操作日志:
  • @foreach (var log in actionLogs.TakeLast(5).Reverse())

    {

    • @log

    }

  • @code {

  • private List items = new List

  • {

  • new Item { Id = 1, Name = "项目1" },

  • new Item { Id = 2, Name = "项目2" },

  • new Item { Id = 3, Name = "项目3" }

  • };

  • private List largeList = Enumerable.Range(1, 100)

  • .Select(i => new Item { Id = i, Name = $"项目{i}" })

  • .ToList();

  • private List actionLogs = new List();

  • private void DeleteItem(int id)

  • {

  • items.RemoveAll(i => i.Id == id);

  • LogAction($"删除了项目 {id}");

  • }

  • private void HandleListItemClick(MouseEventArgs e, int itemId)

  • {

  • // 通过参数 itemId 就知道是哪个按钮被点击了

  • Console.WriteLine($"Clicked item ID: {itemId}");

  • }

  • // 添加 LogAction 方法

  • private void LogAction(string action)

  • {

  • actionLogs.Add($"{DateTime.Now:HH:mm:ss} - {action}");

  • StateHasChanged();

  • }

  • class Item

  • {

  • public int Id { get; set; }

  • public string Name { get; set; } = string.Empty;

  • }

  • }