经典wordpress网站,wordpress 修改语言,现在的网站开发框架,广西住房和城乡建设厅Blazor系统教程
1.认识 Blazor
简单来讲#xff0c;Blazor旨在使用C#来替代JavaScript的Web应用程序的UI框架。其主要优势有#xff1a;
使用C#编写代码#xff0c;这可提高应用开发和维护的效率利用现有的NET库生态系统受益于NET的性能、可靠性和安全性与新式托管平台(如…Blazor系统教程
1.认识 Blazor
简单来讲Blazor旨在使用C#来替代JavaScript的Web应用程序的UI框架。其主要优势有
使用C#编写代码这可提高应用开发和维护的效率利用现有的NET库生态系统受益于NET的性能、可靠性和安全性与新式托管平台(如 Docker) 集成以一组稳定、功能丰富且易用的通用语言、框架和工具为基础来进行生成
Blzaor具有3中托管类型
Blazor Server
Blazor Server 应用程序在服务器上运行所有处理都在服务器上完成UI/DOM 更改通过 SignalR 连接回传给客户端。 Blazor WebAssembly
Blazor WebAssembly应用程序在浏览器中基于WebAssembly的.NET运行时运行客户端。Blazor应用程序及其依赖项和.NET运行时被下载到浏览器中。 Blazor Hybrid
Blazor Hybrid 用于使用混合方法生成本机客户端应用。在 Blazor Hybrid 应用中Razor 组件与任何其他 .NET 代码一起直接在本机应用中而不在 WebAssembly 上运行并通过本地互操作通道基于 HTML 和 CSS 将 Web UI 呈现到嵌入式 Web View 控件。 2.Razor语法和指令
指令
路由定义可以定义多个但是不能重复必须以/开头
page /
导入命名空间
using Microsoft.AspNetCore.Components.Web using static Microsoft.AspNetCore.Components.Web.RenderMode
使用特性
attribute [System.ComponentModel.DataAnnotations.Schema.Table(Table)]
实现接口
implements IDisposable implements IAsyncDisposable
继承父类
inherits ComponentBase
依赖注入 类型-名称
inject IAsyncDisposable varName
使用布局
layout Layout.MainLayout
声明命名空间
namespace myNameSpace
定义泛型
typeparam T1 typeparam T2 where T2:class
代码块
code{}
运算表达式
* 我是注释 *
* 代码区域 *
{var a 1;var b 2;var c a b;
}* 与字符串混用 *
h1C的值(c)个单位/h1* 默认隐式调用为ToString *
pDateTime.Now/p
pDateTime.IsLeapYear(2016)/p* 显式表达式 *
p上一周(DateTime.Now-TimeSpan.FromDays(7))/p* HTML自动转义 *
p(spanHello world/span)/p
p((MarkupString)spanHello world/span)/p* 语句块 *
if (true)
{spantrue/span
}
else
{spanfalse/span
}
for(var i0;i10;i)
{text这里是文本(i)/text
}
try
{throw new InvalidDataException(错误);
}
catch (Exception e)
{pe.Message/p
}
finally
{spanfinally/span
}3. Razo组件
以razor为后缀且首字母大写必须继承自IComponent接口默认继承ComponentBase类。
4.项目结构和路由组件
如果选择Server模式则只有一个项目 如果选择其他模式则会有两个项目BlazorAppBlazorApp.Client 在Program.cs中设置渲染模式
...
//在此设置服务端渲染模式
builder.Services.AddRazorComponents().AddInteractiveServerComponents().AddInteractiveWebAssemblyComponents();
...
...
//设置服务端渲染模式
app.MapRazorComponentsApp().AddInteractiveServerRenderMode().AddInteractiveWebAssemblyRenderMode().AddAdditionalAssemblies(typeof(Client._Imports).Assembly);App.razor为根网页里面有head、body等信息其中在body中指定了Routes
!DOCTYPE html
html langen
headmeta charsetutf-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /base href/ /...HeadOutlet /
/headbodyRoutes /script src_framework/blazor.web.js/script
/body/htmlRoutes中指定了整体布局MainLayout及其他设置
Router AppAssemblytypeof(Program).AssemblyFound ContextrouteDataRouteView RouteDatarouteData DefaultLayouttypeof(Layout.MainLayout) /FocusOnNavigate RouteDatarouteData Selectorh1 //Found
/Router启动时Blazor会检查Assembly属性扫描具有RouteAttribute 的组件Found 标记指定在运行时处理路由的组件RouteView 组件。 此组件接收 RouteData 对象以及来自 URI 或查询字符串的任何参数。 然后它呈现指定的组件及其布局。 可以使用 Found 标记来指定默认布局当所选组件未通过 layout 指令指定布局时将使用该布局。使用 NotFound 标记指定在不存在匹配路由时返回给用户的内容
而在MainLayout中则指定了NavMenu和Body在NavMenu中设置了导航可以导航到定义的page并在设置了Body的地方展示
inherits LayoutComponentBase
div classpagediv classsidebarNavMenu //divmainarticle classcontent px-4Body/article/main
/divNavMenu中设定了导航信息
div classnav-item px-3NavLink classnav-link href MatchNavLinkMatch.Allspan classbi bi-house-door-fill-nav-menu aria-hiddentrue/span Home/NavLink
/divdiv classnav-item px-3NavLink classnav-link hrefcounterspan classbi bi-plus-square-fill-nav-menu aria-hiddentrue/span Counter/NavLink
/divNavLinkMatch.All使用此值时只有在链接的 href 与当前 URL 完全匹配时
NavLinkMatch.Prefix使用此值时当链接的 href 与当前 URL 的第一部分匹配就可以
NavLink会实现一个a链接实现当跳转到指定路由时增加active的class样式也可以自己去设置active样式也就是当匹配后你想设置的样式是什么如 NavLink classnav-link href ActiveClassmyActive MatchNavLinkMatch.All这样我的样式则为myActive。
5. 组件参数
组件参数
组件可以具有参数以供父组件控制使用公共属性和[Parameter]特性来标记组件参数
自定义组件CustomRazor
h1Title/h1code {[Parameter]public string? Title{ set; get; }
}在父组件中使用自定义组件
CustomRazor Title自定义名称/渲染片段
默认单渲染片段
自定义组件CustomRazor渲染片段必须是RenderFragment?类型以ChildContent命名
h1Title/h1p渲染片段/p
pChildContent/p
code {[Parameter]public string? Title{ set; get; }[Parameter] public RenderFragment? ChildContent { set; get; }
}在父组件中使用自定义组件
CustomRazor我是渲染片段
/CustomRazor渲染片段RenderFragment可以呈现任何对象不仅仅是字符串
CustomRazorCustomRazor渲染片段再次使用自定义组件/CustomRazor
/CustomRazor多渲染片段
自定义组件CustomRazor
h1Title/h1p渲染片段/p
pChildContent/p
pOtherChildContent/p
code {[Parameter]public string? Title{ set; get; }[Parameter] public RenderFragment? ChildContent { set; get; }[Parameter] public RenderFragment? OtherChildContent { set; get; }
}使用多个渲染片段
CustomRazorChildContent我是第一个渲染片段/ChildContentOtherChildContent我是第二个渲染片段/OtherChildContent
/CustomRazor6. 导航参数和查询参数
导航参数
page /{id:int}/{name?}PageTitleHome/PageTitlep导航参数是(Id)/p
p名称是(Name)/pcode{[Parameter] public int Id { set; get; }[Parameter] public string? Name { set; get; }
}输入/100/tom 查询参数
page /PageTitleHome/PageTitlep第(Page)页,共(Size)页/pcode{[Parameter][SupplyParameterFromQuery] public int? Page { set; get; }[Parameter][SupplyParameterFromQuery(Name count)] public int? Size { set; get; }
}地址栏输入?page1count100 7. 级联参数
如果子组件中还有子组件当子组件层次比较深时可以使用级联参数让参数沿着层次结构向下自动传递到下级组件在父组件中使用CascadingValue将子组件进行包裹在该标记内呈现的任何组件都能够访问传递的相关参数。
定义子组件
h1我是CustomRazor/h1
h1Title/h1code {[CascadingParameter] string? Title{ set; get; }
}使用子组件
PageTitleHome/PageTitleCascadingValue Value(标题)CustomRazor /
/CascadingValue级联参数会自动匹配类型一样的值比如上面级联参数的类型为string如果具有多个级联参数则会自动匹配最近的一个
CascadingValue Value(外层)CascadingValue Value(内层)CustomRazor //CascadingValue
/CascadingValue如果想要有多个级联参数可以指定名称
h1我是CustomRazor/h1
h1Title1/h1
h1Title2/h1code {[CascadingParameter(Name Title1)] string? Title1 { set; get; }[CascadingParameter(Name Title2)] string? Title2 { set; get; }
}PageTitleHome/PageTitleCascadingValue NameTitle1 Value(外层)CascadingValue NameTitle2 Value(内层)CustomRazor //CascadingValue
/CascadingValue8. 事件和事件参数
事件是一个EventCallback类型切支持泛型参数
h3Event/h3button stylestyle onmouseenterMouseOver onmouseleaveMouseOut按钮/buttoncode {string style;void MouseOver(){style font-size:30px;}void MouseOut(){style String.Empty;}
}自定义事件
首先定义一个Collapse.Razor在该Razor中定义EventCallback类型的属性
button classbtn btn-primary onclickToggleButtonText
/button
div classcollapse (Expand?show:)ChildContent
/divcode {[Parameter] public RenderFragment? ChildContent { get; set; }[Parameter] public EventCallbackbool OnToggle { get; set; }string? ButtonText Expand ? 折叠 : 展开;bool Expand { get; set; }async Task Toggle(){Expand !Expand;//触发传递进来的函数await OnToggle.InvokeAsync(Expand);}
}使用定义的Razor
h3Event/h3Collapse OnToggleToggle要显示的内容
/Collapse
h4message/h4
code {string? message;void Toggle(bool expanded){if (expanded){message 内容已经展开;}else{message ;}}
}9. 模板页
模版页继承自LayoutComponentBase在LayoutComponentBase中有一个属性名称为Body的渲染片段标识要显示的内容。在Router组件中一般设定了默认模版页RouteView DefaultLayouttypeof(Layout.MainLayout) /也可以对不同的组件设置不同的模板页。
创建一个自定义布局
inherits LayoutComponentBaseh3EmptyLayout/h3
divBody/div使用该自定义布局
page /event
layout Layout.EmptyLayout * 只能使用一次 *h3Event/h3
。。。10. 单向绑定和双向绑定
使用bind来进行绑定
pinput bindInputValue bind:eventoninput/*默认是 onchange 标识失去焦点后更新*
/p
pcodeInputeValue/code:InputValue
/p
code{private string? InputValue{set;get;}
}可以使用bing:format来格式化字符串
使用bind:afterInputAfter在失去焦点触发不支持任何参数经常用于输入验证
pinput bindInputValue bind:afterInputAfter/
/p
pmessage
/p
code{private string? InputValue{set;get;}string? message;void InputAfter(){message 输入后得到;}
}bind:after的弊端是不能有参数如果要含有参数则可以使用双向绑定
pinput bind:gettext bind:setOnInput/
/p
code
{string? text;void OnInput(string value){var newValue value ?? string.Empty;text newValue.Length 4 ? Long : newValue;}
}上面都是绑定的字段如果绑定的是属性则可以直接在属性的set和get方法中进行验证操作
下拉框的绑定
plabel选择一个品牌select onchangeSelectedCarsChangedoption valueaA/optionoption valuebB/optionoption valuecC/optionoption valuedD/option/select/label
/pp选择的车SelectedCar
/pcode{public string?SelectedCar{get;set;}void SelectedCarsChanged(ChangeEventArgs e){SelectedCar e.Value?.ToString();}
}11. 自定义组件实现双向绑定
bind只适用于组件内部自定义组件实现双向绑定需按如下步骤
定义绑定属性值
[Parameter] public string? Text { set; get; }
定义一个EventCallback泛型类型的属性名称必须为第一步定义的属性值Changed
[Parameter] public EventCallbackstring TextChanged{ set; get; }
在组件中绑定第一步属性值及设置相应事件
input typetext valueText onchangeOnChange /code {[Parameter] public string? Text { set; get; }[Parameter] public EventCallbackstring TextChanged{ set; get; }Task OnChange(ChangeEventArgs e){Text e.Value?.ToString();TextChanged.InvokeAsync(Text);return Task.CompletedTask;}
}使用定义的组件进行绑定时使用bing-
FormControl bind-TextouterText/FormControl
pouterText
/p
code{string? outerText;
}12. 组件的任意参数
当组件需要定义多个标签属性时可以在定义对应的组件参数但这样过于麻烦。可以借助attributes来实现任意参数
input typetext classform-control (Class) attributesAttributes /code {[Parameter(CaptureUnmatchedValues true)]public Dictionarystring,object? Attributes{ get; set; }
}使用组件
FormControl Attributes(new Dictionarystring, object{[Title]文本框,[style]color:red;font-size:18px
})/FormControl上面代码中使用了[Parameter(CaptureUnmatchedValues true)]可以自动转换为键值对。下面的使用方式与上面的效果完全相同。
FormControl title文本框 stylecolor:red;font-size:18px
/FormControl13. 表单和验证
在web中使用form元素创建表单将input等放入其中实现表单功能Blazor也支持这些但提供了更多的组件
EditForm可以支持和对象直接关联进行双向绑定并提供更多功能
public class WeatherForecast
{public DateTime Date { get; set; }public int TemperatureC { get; set; }public int TemperatureF 32 (int)(TemperatureC / 0.5556);public string Summary { get; set; }
}EditForm ModelcurrentForecastInputDate bind-ValuecurrentForecast.Date/InputDateInputNumber bind-ValuecurrentForecast.TemperatureC/InputNumberInputText bind-ValuecurrentForecast.Summary/InputText
/EditForm
{
private WeatherForecast currentForecast;
}表单验证
EditForm 具有三个在提交后运行的事件
OnSubmit无论验证结果如何只要用户提交表单就会触发此事件。OnValidSubmit当用户提交表单并且他们的输入验证通过时将触发此事件。OnInvalidSubmit当用户提交表单并且他们的输入验证失败时将触发此事件。
EditForm Modelp onsubmitValidateDatah3名字/h3InputText bind-Valuep.Name/InputTexth3年龄/h3InputNumber bind-Valuep.Age min0 max99/InputNumberInput typesubmit value提交/h3message/h3
/EditFormcode {Person p new();string message;private async Task ValidateData(EditContext editContext){var model (Person)editContext.Model;if (model.Age10){message 大于10岁;}}class Person{public string Name{ set; get; }public int Age{ set; get; }}
}错误信息的展示
page /formusing System.ComponentModel.DataAnnotations
PageTitle表单验证/PageTitle
h3表单验证/h3EditForm ModelModel OnValidSubmitSubmitValidDataAnnotationsValidator /* 展示所有的错误信息 *ValidationSummary/div classrow mb-3label classcol-1 col-form-label姓名/labeldiv classcol-11InputText bind-ValueModel.Name classform-control /* 展示单个验证信息 *ValidationMessage For()Model.Name//div/divdiv classrow mb-3label classcol-1 col-form-label密码/labeldiv classcol-11InputText bind-ValueModel.Password classform-control typepassword /* 展示单个验证信息 *ValidationMessage For()Model.Password //div/divbutton typesubmit提交/button
/EditFormcode {class UserInfo{[Required(ErrorMessage 名字不能为空)]public string? Name { get; set; }[Required(ErrorMessage 密码不能为空)]public string? Password { get; set; }}UserInfo Model new();Task SubmitValid(){//数据库查询等操作return Task.CompletedTask;}
}14. 表单验证的进阶
在EditForm外面提交验证
上面的案例中是在EditForm中进行提交并且验证而有时提交是在外面。此时需要EditContext
page /formusing System.ComponentModel.DataAnnotations
PageTitle表单验证/PageTitle
h3表单验证/h3
button classbtn btn-primary onclickSubmitValid提交/button* 定义EditContext *
EditForm EditContextContextDataAnnotationsValidator /ValidationSummary/div classrow mb-3label classcol-1 col-form-label姓名/labeldiv classcol-11InputText bind-ValueModel.Name classform-control /ValidationMessage For()Model.Name//div/divdiv classrow mb-3label classcol-1 col-form-label密码/labeldiv classcol-11InputText bind-ValueModel.Password classform-control typepassword /ValidationMessage For()Model.Password //div/div
/EditFormcode {class UserInfo{[Required(ErrorMessage 名字不能为空)]public string? Name { get; set; }[Required(ErrorMessage 密码不能为空)]public string? Password { get; set; }}UserInfo Model new();//定义EditContext属性EditContext Context { get; set; }public Form(){Context new EditContext(Model);}Task SubmitValid(){//查询验证是否通过bool isValid Context.Validate();//数据库查询等操作return Task.CompletedTask;}
}自定义错误消息
上面案例中在EditForm内部使用了ValidationSummary/和ValidationMessage/来显示错误信息这些组件必须放置在EditForm内部如果在外部自定义错误信息则可以使用Context.GetValidationMessages();
page /formusing System.ComponentModel.DataAnnotations
using System.Reflection
PageTitle表单验证/PageTitle
h3表单验证/h3
button classbtn btn-primary onclickSubmitValid提交/button* 定义EditContext *
EditForm EditContextContextDataAnnotationsValidator /div classrow mb-3label classcol-1 col-form-label姓名/labeldiv classcol-11InputText bind-ValueModel.Name classform-control //div/divdiv classrow mb-3label classcol-1 col-form-label密码/labeldiv classcol-11InputText bind-ValueModel.Password classform-control typepassword //div/div
/EditForm
if (Errors.Any())
{div classalert alert-dangerulforeach (var message in Errors){limessage/li}/ul/div
}GetValidation(nameof(Model.Name));code {class UserInfo{[Required(ErrorMessage 名字不能为空)]public string? Name { get; set; }[Required(ErrorMessage 密码不能为空)]public string? Password { get; set; }}UserInfo Model new();//定义EditContext属性EditContext Context { get; set; }IEnumerablestring Errors { get; set; } [];public Form(){Context new EditContext(Model);}Task SubmitValid(){//查询验证是否通过bool isValid Context.Validate();if (!isValid){Errors Context.GetValidationMessages();return Task.CompletedTask;}//数据库查询等操作return Task.CompletedTask;}//获得单个属性验证消息string? GetValidation(string name){FieldIdentifier fieldIdentifier Context.Field(name);if (!Context.IsValid(fieldIdentifier)){var property Model?.GetType()?.GetProperty(fieldIdentifier.FieldName);var requiredAtr property?.GetCustomAttributeRequiredAttribute();var value property?.GetValue(Model);if (!requiredAtr.IsValid(value)){return requiredAtr.ErrorMessage;}}return string.Empty;}
} FormCssClassProvider
上面案例中如果出现错误则文本框边框会变为红色这是因为当有错误时会添加invalid 的css类样式如果想自定义样式则可使用FormCssClassProvider
public class FormCssClassProvider : Microsoft.AspNetCore.Components.Forms.FieldCssClassProvider
{public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier){//如果没有任何改动if (!editContext.IsModified()){return string.Empty;}var valid editContext.IsValid(fieldIdentifier);return valid ? is-valid : is-invalid;}
}
只需设置
public Form()
{Context new EditContext(Model);Context.SetFieldCssClassProvider(new FormCssClassProvider());
}此时文本框中会加上对钩和感叹号
15. 组件的生命周期 16. 泛型组件
基本使用
泛型组件类似于C#中的泛型类使用流程同样是先定义泛型参数然后使用
定义泛型组件
h3泛型组件/h3
typeparam TValue where TValue:struct
typeparam TTextp值是Value,类型是typeof(TValue)
/p
p值是Text,类型是typeof(TText)
/p
code {[Parameter] public TValue Value { set; get; }[Parameter] public TText Text { set; get; }
}可直接声明泛型类型也可自动推断
Genaric TValueint Value100 TTextstring TextouterText /
Genaric Value100 TextouterText /code{string outerText字符串;
}案例根据绑定数据类型改变input的type
泛型组件Genaric.razor
h3泛型组件/h3
typeparam TValueinput typeInputType valueCurrentValue oninputOnChange/code {[Parameter] public TValue? Value { set; get; }[Parameter] public EventCallbackTValue? ValueChanged { get; set; }string? CurrentValue{ set; get; }Task OnChange(ChangeEventArgs e){var tmpValue e.Value;if (tmpValue is null){return Task.CompletedTask;}//转换var newValue Convert.ChangeType(tmpValue, typeof(TValue));Value (TValue)newValue;ValueChanged.InvokeAsync(Value);CurrentValue BindConverter.FormatValue(tmpValue)?.ToString();return Task.CompletedTask;}//判断类型string? InputType Value switch{double or float or int or decimal number,DateOnly or DateTime or DateTimeOffset date,_ text};
}
使用泛型组件
ulli数字Genaric bind-Valuenum//lili文本Genaric bind-Valuetext //lili时间Genaric bind-Valuetime //li
/ulcode{string text;float num;DateTime time DateTime.Now;
}17. 模板化组件
模版化组件通常和泛型组件相结合案例需展示数据列表展示的形式及数据需可自定义。
typeparam TDataif (Datas is not null)
{table classtabletheadtrHeaderTemplage/tr/theadtbodyforeach (var item in Datas){trRowTemplate?.Invoke(item)/tr}/tbody/table
}code {[Parameter] public IEnumerableTData Datas{ set; get; }[Parameter] public RenderFragmentTData? RowTemplate { set; get; }[Parameter] public RenderFragment? HeaderTemplage { set; get; }
}
上面代码中Datas保存数据但是TData类型不确定在tbody中展示时不确定里面有什么数据所以需要用户显示方式。同样表头thead同样也不确定需要展示哪些表头属性需要用户来确定
使用模版组件
Genaric DatasUsersHeaderTemplagethId/thth名称/th/HeaderTemplageRowTemplatetdcontext.Id/tdtdcontext.Name/td/RowTemplate
/Genariccode{class User{public int Id { get; set; }public string? Name { get; set; }}IEnumerableUser Users new ListUser{new(){ Id1, Name张三},new(){ Id2, Name李四},new(){ Id3, Name王五},new(){ Id4, Name赵六}};
}在RowTemplate中的context代表泛型类型和this含义用法有些相同 18. 渲染模式
名称描述呈现位置交互静态静态服务器端呈现静态 SSR服务器❌否交互式 Blazor Server使用 Blazor Server 的交互式服务器端呈现交互式 SSR。服务器✔️是交互式 WebAssembly使用 Blazor WebAssembly 的客户端呈现 (CSR)。客户端✔️是交互式自动先使用 Blazor Server 然后使用 CSR 。服务器然后客户端
Blazor Server可兼容WebAssembly反之不可以。也就是说在Server模式下的组件可放置WebAssembly组件反之不行。
需要在Program中增加相应中间件
//在此设置服务端渲染模式
builder.Services.AddRazorComponents().AddInteractiveServerComponents().AddInteractiveWebAssemblyComponents();//设置服务端渲染模式
app.MapRazorComponentsApp().AddInteractiveServerRenderMode().AddInteractiveWebAssemblyRenderMode().AddAdditionalAssemblies(typeof(Client._Imports).Assembly);如果在创建工程时设置了全局则在App.razor中会自动设置渲染模式渲染模式是向下传递的。也就是如果子组件没有设置渲染模式则继承父组件的渲染模式。 !DOCTYPE html
html langen
head...HeadOutlet rendermodeInteractiveAuto /
/head
bodyRoutes rendermodeInteractiveAuto /script src_framework/blazor.web.js/script
/body
/html也可在组件中直接使用rendermode InteractiveServer来指定渲染模式也可在外部使用Genaric rendermodeInteractiveWebAssembly进行指定如果在外部使用则在内部不能指定渲染模式。
当组件中含有RenderFragment参数这种参数不可序列化如果指定渲染模式时会报错遇到这种问题需要在其外层包装一下就可以
19. CSS隔离和代码隔离
CSS隔离
一般在app.css中进行定义但是不利于管理
可以定义一个组件名称.css的文件如GenaricTable.razor.css
注意在App.razor中一定要引用link relstylesheet href工程名.styles.css /
代码隔离
可以定义一个组件名称.cs的文件如GenaricTable.razor.cs并将类设置为partial
20. 异常处理
当程序遇到未捕获的异常时会在底部弹出如下提示。 可在MainLayout中设置错误提示
article classcontent px-4ErrorBoundaryBody/ErrorBoundary
/article默认错误提示 可自定义错误样式
article classcontent px-4ErrorBoundaryErrorContent出现错误context.Message/ErrorContentChildContentBody/ChildContent /ErrorBoundary
/article21. 流式渲染
Count:countcode {int count 0;async Task DoCount(){for (int i 0; i 10; i){await Task.Delay(1000);count;StateHasChanged();}}// 页面会一直卡着直到运行完DoCountprotected override async Task OnInitializedAsync(){await DoCount();}
}流式渲染解决了这个问题
只需要加上attribute [StreamRendering]即可实现 22. 预呈现模式
预呈现是先呈现一部分尽快输出页面的HTML UI让用户感觉提升了响应速度。
定义组件Perrender.razor
div classcarddiv classcard-bodyh2预呈现 Title/h2hr/pHello world/pbutton classbtn btn-success提交/buttonif (_isComplete){h3渲染完成/h3}/div/divcode {[Parameter] public string? Title{ set; get; }bool _isComplete;protected override async Task OnInitializedAsync(){await Task.Delay(2000);_isComplete true;}
}使用组件
第一个关闭预呈现第二个打开预呈现
Perrender Title开启 rendermodenew InteractiveWebAssemblyRenderMode(false)/
------------------------------
Perrender Title关闭 rendermodenew InteractiveWebAssemblyRenderMode(true) /
------------------------------如果使用预呈现在server模式中需要注意状态保留问题。
23. C# 和 JS 的互操作
C#调用JS
直接写js
button onclickjavascript:alert(提示)提示/button
使用IJSRuntime
注入IJSRuntime
inject IJSRuntime JS
button onclickAlert提示/button
button onclickPropmt弹出框/button
输入的名称是 Valuecode {async Task Alert(){//带Void的表示无返回值//第一个参数为js的函数名后面的参数为可变参数列表await JS.InvokeVoidAsync(hello, 参数);}string? Value{ set; get; }async Task Propmt(){var value await JS.InvokeAsyncstring(prompt, 请输入名字);Value value;}
}调用自定义JS函数
在项目中wwwroot中增加js文件并在APP.razor中引用该文件script srcapp.js/script
//js中定义的函数
function hello() {alert(我是自定义hello函数);
}在C#中调用
async Task Alert()
{await JS.InvokeVoidAsync(hello, 参数);
} JS调用C#中的函数
静态方法
用C#写静态方法
public class Functions
{[JSInvokable]public static int Add(){return 1 5;}[JSInvokable]public static Taskint AddAsync(){return Task.FromResult(110);}
}js调用写的静态方法
function add() {//参数1C#函数所在的程序集 参数2函数名参数3可变参数列表//DotNet是固定的let result DotNet.invokeMethod(BlazorApp2.Client, Add);console.log(result);
}function addAsync() {DotNet.invokeMethodAsync(BlazorApp2.Client, AddAsync).then(rconsole.log(r));
}这种方式非常不推荐如果有多个.net运行时会导致错误
普通方法
创建普通方发
public class Functions
{[JSInvokable]public int Add(){return 1 5;}[JSInvokable]public Taskint AddAsync(){return Task.FromResult(1 10);}
}在razor页面中定义方法
inject IJSRuntime JS
button onclickAdd加/button
button onclickAddAsync加-异步/button
code {async Task Add(){ //获取引用并调用js中定义的方法js中需要有引用参数var dotReference DotNetObjectReference.Create(new Functions());await JS.InvokeVoidAsync(add, dotReference);}async Task AddAsync(){var dotReference DotNetObjectReference.Create(new Functions());await JS.InvokeVoidAsync(addAsync, dotReference);}
}js中调用C#中的方法
function add(p) {//不需要传递程序集,p为dot引用里面有相关函数信息let result p.invokeMethod(Add);console.log(result);
}function addAsync(p) {p.invokeMethodAsync(AddAsync).then(r console.log(r));
}24. 渲染树
每个组件都是继承自ComponentBase类完全可以自定义类来实现Razor组件的功能
public class Button : ComponentBase
{[Parameter] public RenderFragment? ChildContent { get; set; }[Parameter]public bool Outline { get; set; }[Parameter] public string? Tag { get; set; } button;protected override void BuildRenderTree(RenderTreeBuilder builder){//button/buttonbuilder.OpenElement(0, Tag);builder.AddAttribute(1, class, $btn btn-{(Outline?outline-:)}success);builder.AddAttribute(2, onclick, EventCallback.Factory.Create(this, (){。。。}));builder.AddContent(10, ChildContent);builder.CloseElement();}
}实现上面的类后可以像使用组件一样来使用
Button填充按钮/Button
Button Outline边框按钮/Button
Button TagspanSpan 按钮/Button25. 与 WEB API 的交互
在用Auto模式或者WebAssembly模式时往往需要获取远程数据这时就涉及到与Web API的交互。
首先要确定Web API允许跨域一定要在工程名.Client项目下的Program.cs中注册HTTP服务
static async Task Main(string[] args)
{var builder WebAssemblyHostBuilder.CreateDefault(args);builder.Services.AddScoped(sp new HttpClient { BaseAddress new Uri(http://localhost:5041/) });await builder.Build().RunAsync();
}WebAPI的返回形式一般是Json数据首先声明对应Json数据的类然后注入HttpClient。并利用上文中模版化组件进行展示
inject HttpClient client
button onclickGetDataAsync获取远程数据/buttonif (Data is null)
{div数据加载中/div
}
else
{Genaric DatasDataHeaderTemplageth日期/thth摄氏度/thth华氏度/thth说明/th/HeaderTemplageRowTemplatetdcontext.Date/tdtdcontext.TemperatureC/tdtdcontext.TemperatureF/tdtdcontext.Summary/td/RowTemplate/Genaric
}code {public class WeatherForecast{public DateOnly Date { get; set; }public int TemperatureC { get; set; }public int TemperatureF { get; set; }public string? Summary { get; set; }}IEnumerableWeatherForecast? Data{ set; get; }async Task GetDataAsync(){Data await client.GetFromJsonAsyncIEnumerableWeatherForecast(WeatherForecast);}
}26. 部署到 IIS
.net Core运行时下载Hosting Bundle设置发布路径Ip、端口号等确认设置模块中是否含有AspNetCoreModuleV2确认处理程序映射是否含有aspNetCore设置应用程序池将.net Clr中.net CLR版本设置为无托管代码