免费网站建站平台,微信小程序商店wordpress做,电脑怎么打不开建设银行网站,郑州正规的男科医院有哪些冠纶诚纶大家好#xff0c;我是码农刚子。上一章介绍了Blazor的简介#xff0c;开发工具及环境#xff0c;基本语法和一些示例。接下来我们继续了解Blazor 组件相关的基础知识#xff0c;希望对你有所帮助。 1、组件生命周期 1.简介 Blazor的生命周期与React组件的生命周期…冠纶诚纶大家好我是码农刚子。上一章介绍了Blazor的简介开发工具及环境基本语法和一些示例。接下来我们继续了解Blazor 组件相关的基础知识希望对你有所帮助。1、组件生命周期1.简介Blazor的生命周期与React组件的生命周期类似也分为三个阶段初始化阶段、运行中阶段和销毁阶段其相关方法有10个包括设置参数前、初始化、设置参数之后、组件渲染后以及组件的销毁但是这些方法有些是重复的只不过是同步与异步的区别。2.图解首先将结果图呈现代码位于第3部分Blazor生命周期方法主要包括1设置参数前SetParametersAsync2初始化OnInitialized/OnInitializedAsync3设置参数后OnParametersSet/OnParametersSetAsync4组件渲染呈现后OnAfterRender/OnAfterRenderAsync5判断是否渲染组件ShouldRender6组件删除前Dispose7通知组件渲染StateHasChanged在所有生命周期函数中有以下需要注意的点1前5种方法的声明都是virtual除SetParametersAsync为public外其他的都是protected。2OnAfterRender/OnAfterRenderAsync方法有一个bool类型的形参firstRender用于指示是否是第一次渲染即组件初始化时的渲染。3同步方法总是先于异步方法执行。4Dispose函数需要通过使用implements指令实现IDisposable接口来实现。5StateHasChanged无法被重写可以被显示调用以便强制实现组件刷新如果ShouldRender返回true并且Blazor认为需要刷新当组件状态更改时不必显示调用此函数也可导致组件的重新渲染如果ShouldRender返回true因为其已经在ComponentBase内部的处理过程第一次初始化设置参数时、设置参数后和DOM事件处理等中被调用。3.代码示例设置参数时 (SetParametersAsync 设置由组件的父组件在呈现树或路由参数中提供的参数。每次调用 ParameterView 时方法的 参数都包含该组件的SetParametersAsync值集。 通过重写 SetParametersAsync 方法C#代码可以直接与 ParameterView 参数交互。page /set-params-async/{Param?}Set Parameters AsyncSet Parameters Async Examplemessagecode {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 中的初始参数后初始化此时将调用这些方法。如果使用同步父组件初始化则保证父组件初始化在子组件初始化之前完成。 如果使用异步父组件初始化则无法确定父组件和子组件初始化的完成顺序因为它取决于正在运行的初始化代码。对于同步操作重写 OnInitializedpage /on-initOn InitializedOn Initialized Examplemessagecode {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;}如果自定义基类与自定义初始化逻辑一起使用需在基类上调用 OnInitializedAsyncprotected override async Task OnInitializedAsync(){await ...await base.OnInitializedAsync();}设置参数之后 (OnParametersSet 或 OnParametersSetAsync 在以下情况下调用在 OnInitialized 或 OnInitializedAsync 中初始化组件后。当父组件重新呈现并提供以下内容时至少一个参数已更改时的已知或基元不可变类型。复杂类型的参数。 框架无法知道复杂类型参数的值是否在内部发生了改变因此如果存在一个或多个复杂类型的参数框架始终将参数集视为已更改。在组件路由中不能同时对DateTime参数使用datetime路由约束并将该参数设为可选。 因此以下 OnParamsSet 组件使用两个 page 指令来处理具有和没有 URL 中提供的日期段的路由。page /on-params-setpage /on-params-set/{StartDate:datetime}On Parameters SetOn Parameters Set ExamplePass a datetime in the URI of the browsers address bar.For example, add/1-1-2024to the address.messagecode {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 ...}如果自定义基类与自定义初始化逻辑一起使用需在基类上调用 OnParametersSetAsyncprotected 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-renderinject ILogger LoggerAfter RenderAfter Render ExampleLog 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){...}如果自定义基类与自定义初始化逻辑一起使用需在基类上调用 OnAfterRenderAsyncprotected 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-2inherits BlazorRocksBase2inject ILogger LoggerBlazor Rocks!Blazor Rocks! Example 2BlazorRocksTextcode {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; }}}2. 双向数据绑定双向绑定允许数据在组件和UI之间双向流动UI变化自动更新数据源数据源变化自动更新UI。基本语法...完整示例双向绑定示例用户名:显示: userName邮箱:显示: email年龄:显示: age城市:请选择北京上海广州深圳选择: selectedCity是否同意协议:(isAgreed ? 已同意 : 未同意)汇总信息:用户名: userName邮箱: email年龄: age城市: selectedCity同意协议: isAgreedcode {private string userName string.Empty;private string email string.Empty;private int age 0;private string selectedCity string.Empty;private bool isAgreed false;}3. 绑定事件控制3.1 绑定特定事件默认情况下bind 在失去焦点时更新。可以使用 bind:event 指定触发事件实时绑定示例placeholder输入搜索内容... /实时搜索: searchText默认绑定: normalTextcode {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;}4. 自定义组件双向绑定在自定义组件中实现双向绑定子组件LabelvalueValueοninputHandleInputclassform-control AdditionalClassplaceholderPlaceholder /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-ValueuserNameLabel用户名Placeholder请输入用户名 /bind-ValueemailLabel邮箱Placeholder请输入邮箱地址ValidationMessage(IsValidEmail ? : 邮箱格式不正确) /用户名: userName邮箱: emailcode {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数据源变化时显示数据、计算属性双向绑定bindproperty失去焦点时表单输入、用户交互实时双向bindproperty bind:eventoninput输入时实时更新搜索框、实时验证自定义绑定bind-Valueproperty自定义事件触发自定义表单组件3、事件处理1. 基本事件处理1.1 单击事件单击事件示例点击我按钮 1按钮 2按钮 3最后点击的按钮: lastClickedButton点击次数: clickCountcode {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();}}2. 表单事件处理2.1 输入事件表单事件处理输入文本:οnchangeHandleChangeclassform-controlplaceholder输入内容... /实时输入: inputValue | 变化事件: changeValue选择选项:请选择选项一选项二选项三选择的值: selectedValue同意条款状态: (isChecked ? 已选中 : 未选中)用户名:邮箱:提交表单表单数据:System.Text.Json.JsonSerializer.Serialize(user, new System.Text.Json.JsonSerializerOptions { WriteIndented true })提交状态: submitStatuscode {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;}}3. 鼠标和键盘事件3.1 鼠标事件鼠标事件οnmοusedοwnHandleMouseDownοnmοuseupHandleMouseUpοnmοusemοveHandleMouseMoveοnmοuseοverHandleMouseOverοnmοuseοutHandleMouseOutοnclickHandleAreaClickοndblclickHandleDoubleClickstylewidth: 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 键盘事件键盘事件οnkeyupHandleKeyUpοnkeypressHandleKeyPressclassform-controlplaceholder在这里输入并观察键盘事件... /键盘事件日志:foreach (var log in keyEventLogs.TakeLast(10).Reverse()){log}最后按下的键: lastKeyCtrl 按下: (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(快捷键: CtrlS, 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);}}4. 焦点和剪贴板事件焦点和剪贴板事件焦点测试输入框:οnblurHandleBlurclassform-controlplaceholder点击获取焦点点击别处失去焦点 /复制粘贴测试:p oncutHandleCut/p p οnpasteHandlePaste/p p classform-control/p p rows3/p p placeholder在这里测试复制、剪切、粘贴操作这是一些测试文本事件状态:焦点状态:(hasFocus ? 有焦点 : 无焦点)最后操作: lastOperation剪贴板内容: clipboardContentcode {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();}}5. 自定义事件处理5.1 事件参数封装自定义事件处理父级区域点击会触发onclick:stopPropagationclassbtn btn-primary子按钮点击不会冒泡onclick:preventDefaultclassbtn 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(异步操作完成);}}6. 事件处理最佳实践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;}}