ASP.NET Core Blazor Webassembly 之 组件

关于组件

现在前端几大轮子全面组件化。组件让我们可以对常用的功能进行封装,以便复用。组件这东西对于搞.NET的同学其实并不陌生,以前ASP.NET WebForm的用户控件其实也是一种组件。它封装html代码,封装业务逻辑,对外提供属性事件等信息,它完完全全就是个组件,只是用户控件跑在服务端,而现在的组件大多数直接跑在前端。现在Blazor Webassembly微软正式把组件带到前端,让我们看看它是怎么玩的。

第一个组件

废话不多说下面开始构建第一个组件。这个组件很简单就是绿色的面板加一个标题的容器,我们就叫它GreenPanel吧。

新建Blazor Webassembly项目

前几天的build大会,Blazor Webassembly已经正式release了。我们更新最新版的Core SDK就会安装正式版的模板。

新建项目选Blazor Webassembly App项目模板

新建GreenPanel组件

在pages命令下新建一个文件夹叫做components,在文件夹下新建一个razor组件,命名为GreenPanel.razor。

注意:组件的命名必须大写字母开头

添加代码如下:

<div class="green-panel"><div class="title">Green panel</div><div class="content"></div>
</div>
<style>.green-panel{background-color: green;height:400px;width:400px;}.green-panel .title {border-bottom:1px solid #333;height:30px;}.green-panel .content {}
</style>
@code { override void OnInitialized(){base.OnInitialized();}
}

一个组件主要是由html,style ,code等组成。html,style用来控制ui表现层,code用来封装逻辑。

注意:Blazor目前没有样式隔离技术,所以写在组件内的style有可能会影响其他html元素

使用组件

使用组件跟其他框架大体是相同的,直接在需要使用的地方使用以我们组件名作为一个html元素插入:
如果不在同一层目录下,则需要导入命名空间。在_Imports.razor文件内引用组件的命名空间:

...
@using BlazorWasmComponent.Components

在index页面使用组件:

<GreenPanel></GreenPanel>

运行一下:

组件类

每个组件最后都会编译成一个C#类,让我们用ILSPy看看一眼长啥样:

// BlazorWasmComponent.Components.GreenPanel
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
public class GreenPanel : ComponentBase
{protected override void BuildRenderTree(RenderTreeBuilder __builder){__builder.AddMarkupContent(0, "<div class=\"green-panel\">\r\n    <div class=\"title\">\r\n        Green panel\r\n    </div>\r\n    <div class=\"content\">\r\n    </div>\r\n</div>\r\n\r\n");__builder.AddMarkupContent(1, "<style>\r\n    .green-panel{\r\n        background-color: green;\r\n        height:400px;\r\n        width:400px;\r\n    }\r\n    .green-panel .title {\r\n        border-bottom:1px solid #333;\r\n        height:30px;\r\n    }\r\n    .green-panel .content {\r\n    }\r\n</style>");}protected override void OnInitialized(){base.OnInitialized();}
}

GreenPanel组件会编译成一个GreenPanel类,继承自ComponentBase基类。里面有几个方法:

  1. BuildRenderTree 用来构建html,css等ui元素

  2. 其它code部分会也会被合并到这个类里面

生命周期

了解组件声明周期对我们使用组件有很大的帮助。一个组件的生命周期主要依次以下几个阶段:

  1. OnInitialized、OnInitializedAsync

  2. OnParametersSet、OnParametersSetAsync

  3. OnAfterRender、OnAfterRenderAsync

  4. Dispose

如果要在每个生命阶段插入特定的逻辑,请重写这些方法:

@implements IDisposable
@code {protected override void OnInitialized(){Console.WriteLine("OnInitialized");base.OnInitialized();}protected override Task OnInitializedAsync(){Console.WriteLine("OnInitializedAsync");return base.OnInitializedAsync();}protected override void OnParametersSet(){Console.WriteLine("OnParametersSet");base.OnParametersSet();}protected override Task OnParametersSetAsync(){Console.WriteLine("OnParametersSetAsync");return base.OnParametersSetAsync();}protected override void OnAfterRender(bool firstRender){Console.WriteLine("OnAfterRender");base.OnAfterRender(firstRender);}protected override Task OnAfterRenderAsync(bool firstRender){Console.WriteLine("OnAfterRenderAsync");return base.OnAfterRenderAsync(firstRender);}public void Dispose(){Console.WriteLine("Dispose");}
}

注意:组件默认并不继承IDisposable接口,如果要重写Dispose方法请手工使用@implements方法继承接口IDisposable

运行一下,并且切换一下页面,使组件销毁,可以看到所有生命周期方法依次执行:

组件属性

我们定义组件总是免不了跟外部进行交互,比如从父组件接受参数,或者把自身的数据对外暴露。我们可以使用[Parameter]来定义一个组件的属性。这里叫做Parameter,估计是为了跟C#里的属性(property,attribute)进行区分。
对我们的GreenPanel组件进行改进,支持从外部定义标题的内容:

<div class="green-panel"><div class="title">@Title</div><div class="content"></div>
</div>
<style>.green-panel {background-color: green;height: 400px;width: 400px;}.green-panel .title {border-bottom: 1px solid #333;height: 30px;}.green-panel .content {}
</style>
@code {[Parameter]public string Title { get; set; }protected override void OnInitialized(){base.OnInitialized();}
}

在父组件使用:

@page "/"
<GreenPanel Title="Panel A"></GreenPanel>

运行一下:

上面传递的是简单类型String,下面让我们试试传递复杂类型的数据进去。我们继续对GreenPanel改造。改造成ColorPanel,它接受一个Setting对象来设置标题跟背景颜色。

定义Setting类:

  public class PanelSetting{public string Title { get; set; }public string BgColor { get; set; }}

定义ColorPanel:

<div class="green-panel"><div class="title">@Setting.Title</div><div class="content"></div>
</div>
<style>.green-panel {background-color: @Setting.BgColor;height: 400px;width: 400px;}.green-panel .title {border-bottom: 1px solid #333;height: 30px;}.green-panel .content {}
</style>
@using BlazorWasmComponent.models;
@code {[Parameter]public PanelSetting Setting { get; set; }protected override void OnInitialized(){base.OnInitialized();}
}

在父组件使用:

@page "/"
<p>@PanelSetting.Title</p>
<p>@PanelSetting.BgColor</p>
<ColorPanel Setting="PanelSetting"></ColorPanel>
@using BlazorWasmComponent.models;
@code{  public PanelSetting PanelSetting { get; set; }protected override void OnInitialized(){PanelSetting = new PanelSetting{BgColor = "Red",Title = "Panel RED"};base.OnInitialized();}
}

运行一下:

注意一篇WebAssembly初探里有个错误,当时认为这个属性是单向数据流,经过试验子组件对父组件传入的数据源进行修改的时候其实是会反应到父组件的,只是如果你使用@符号绑定数据的时候并不会像angularjs,vue等立马进行刷新。关于这个事情感觉可以单独写一篇,这里就不细说了。

组件事件

我们的组件当然也可以提供事件,以供外部订阅,然后从内部激发来通知外部完成业务逻辑,实现类似观察者模式。继续改造ColorPanel,当点击时候对外抛出事件。
使用EventCallback、EventCallback< T > 来定义事件:

<div class="green-panel" @onclick="DoClick"><div class="title">@Setting.Title</div><div class="content"></div>
</div>
<style>.green-panel {background-color: @Setting.BgColor;height: 400px;width: 400px;}.green-panel .title {border-bottom: 1px solid #333;height: 30px;}.green-panel .content {}
</style>
@using BlazorWasmComponent.models;
@code {[Parameter]public PanelSetting Setting { get; set; }[Parameter]public EventCallback OnClick { get; set; }protected override void OnInitialized(){base.OnInitialized();}public void DoClick(){OnClick.InvokeAsync(null);}
}

父组件订阅事件:

@page "/"
<p>子组件点击次数:@ClickCount
</p>
<ColorPanel Setting="PanelSetting" OnClick="HandleClick"></ColorPanel>
@using BlazorWasmComponent.models;
@code{  public PanelSetting PanelSetting { get; set; }public int ClickCount { get; set; }protected override void OnInitialized(){PanelSetting = new PanelSetting{BgColor = "Red",Title = "Panel RED"};base.OnInitialized();}private void HandleClick(){ClickCount++;}
}

运行一下,并点击子组件,父组件的计数器会被+1:

子内容

当我们定义容器级别的组件时往往需要往组件内传递子内容。比如我们的ColorPanel明显就有这种需求,这个Panel内部会被放上其它元素或者其它组件,这个时候我们可以使用ChildContent属性来实现。

<div class="green-panel" @onclick="DoClick"><div class="title">@Setting.Title</div><div class="content">@ChildContent</div>
</div>
<style>.green-panel {background-color: @Setting.BgColor;height: 400px;width: 400px;}.green-panel .title {border-bottom: 1px solid #333;height: 30px;}.green-panel .content {}
</style>
@using BlazorWasmComponent.models;
@code {[Parameter]public PanelSetting Setting { get; set; }[Parameter]public EventCallback OnClick { get; set; }[Parameter]public RenderFragment ChildContent { get; set; }protected override void OnInitialized(){base.OnInitialized();}public void DoClick(){OnClick.InvokeAsync(null);}
}

定义一个类型为RenderFragment名称为ChildContent的属性,然后在html内使用@ChildContent来指代它。这样子内容就会被替换到指定的位置。
父组件使用,我们给ColorPanel的内部设置一个文本框吧:

@page "/"
<p>子组件点击次数:@ClickCount
</p>
<ColorPanel Setting="PanelSetting" OnClick="HandleClick">输入框: <input />
</ColorPanel>
@using BlazorWasmComponent.models;
@code{  public PanelSetting PanelSetting { get; set; }public int ClickCount { get; set; }protected override void OnInitialized(){PanelSetting = new PanelSetting{BgColor = "Red",Title = "Panel RED"};base.OnInitialized();}private void HandleClick(){ClickCount++;}
}

运行一下看看我们的文本框会不会出现在panel内部:

@ref

因为我们的组件使用是在html内,当你在@code内想要直接通过代码操作子组件的时候可以给子组件设置@ref属性来直接获取到子组件的对象。继续改造ColorPanel,在它初始化的时候生产一个ID。

<div class="green-panel" @onclick="DoClick"><div class="title">@Setting.Title</div><div class="content">@ChildContent</div>
</div>
<style>.green-panel {background-color: @Setting.BgColor;height: 400px;width: 400px;}.green-panel .title {border-bottom: 1px solid #333;height: 30px;}.green-panel .content {}
</style>
@using BlazorWasmComponent.models;
@code {public string ID { get; set; }[Parameter]public PanelSetting Setting { get; set; }[Parameter]public EventCallback OnClick { get; set; }[Parameter]public RenderFragment ChildContent { get; set; }protected override void OnInitialized(){ID = Guid.NewGuid().ToString();base.OnInitialized();}public void DoClick(){OnClick.InvokeAsync(null);}
}

修改父组件,添加一个按钮,当点击的时候直接获取子组件的Id:

@page "/"
<p>子组件ID:@subId
</p>
<ColorPanel Setting="PanelSetting" OnClick="HandleClick" @ref="colorPanel">输入框: <input />
</ColorPanel>
<button @onclick="GetSubComponentId" class="btn btn-info">获取子组件ID</button>
@using BlazorWasmComponent.models;
@code{  private string subId;private ColorPanel colorPanel;public PanelSetting PanelSetting { get; set; }public int ClickCount { get; set; }protected override void OnInitialized(){PanelSetting = new PanelSetting{BgColor = "Red",Title = "Panel RED"};base.OnInitialized();}private void HandleClick(){ClickCount++;}private void GetSubComponentId (){this.subId = colorPanel.ID;}
}

运行一下:

@key

当使用循环渲染组件的时候请在组件上使用@key来加速Blazor的diff算法。有了key就可以快速的区分哪些组件是可以复用的,哪些是要新增或删除的,特别是在对循环列表插入对象或者删除对象的时候特别有用。如果使用过vue就应该很容易明白有了key可以降低虚拟dom算法的复杂度,在这里猜测blazor内部应该也是类似的算法。

@page "/"
@foreach (var key in List)
{<ColorPanel @key="key" Setting="PanelSetting"></ColorPanel>
}
@using BlazorWasmComponent.models;
@code{  public List<String> List = new List<string>{Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString()};public PanelSetting PanelSetting { get; set; }protected override void OnInitialized(){PanelSetting = new PanelSetting{BgColor = "Red",Title = "Panel RED"};base.OnInitialized();}
}

太晚了就这样吧,喜欢的话请点个赞,谢谢!

相关内容:
ASP.NET Core Blazor 初探之 Blazor WebAssembly

ASP.NET Core Blazor 初探之 Blazor Server

关注我的公众号一起玩转技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

122. 买卖股票的最佳时机 II008(贪心算法+思路)

一&#xff1a;题目 给定一个数组 prices &#xff0c;其中 prices[i] 是一支给定股票第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;。 注意&#xff1a;你不能同时参与多笔交易&#xf…

广东省计算机应用考试题,广东省计算机等级考试一级试题

广东省计算机等级考试一级试题一、理论部分 (共20分&#xff0c;每空1分)将正确答案填写在考试文件夹中“第一题答案.XLS”的Sheet1相应的位置上1. 解释程序的功能是 ( )。A解释执行高级语言源程序 B将高级语言源程序翻译成目标程序C解释执行汇编语言源程序 D将汇编语言源程序翻…

麒麟系统兼容安卓生态 弥补生态短板

日前&#xff0c;麒麟软件发布了银河麒麟操作系统最新版本V10。据媒体报道&#xff0c;麒麟软件是国内唯一一个通过CMMI5级质量评估的操作系统企业&#xff0c;在XC市场份额占有率达到70%以上。麒麟V10系统可以兼容安卓生态。集成了自研的UKUI桌面环境&#xff0c;支持多壁纸、…

763. 划分字母区间009(贪心算法+思路+详解+图示)

一&#xff1a;题目&#xff1a; 字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。 示例&#xff1a; 输入&#xff1a;S "ababcbacadefegdehijhklij" 输出…

初识ABP vNext(4):vue用户登录菜单权限

点击上方蓝字"小黑在哪里"关注我吧登录菜单权限运行测试前言上一篇已经创建好了前后端项目&#xff0c;本篇开始编码部分。开始几乎所有的系统都绕不开登录功能&#xff0c;那么就从登录开始&#xff0c;完成用户登录以及用户菜单权限控制。登录首先用户输入账号密码…

满汉楼(德鲁伊连接池+DBUtils+DAO+Mysql)保姆级别分析+代码实现

一&#xff1a;需求 1.完成一个酒店后台管理系统&#xff0c;当然只是模拟&#xff0c;在控制台就行模拟 2.实现管理人员的登录和酒店成本账单的查看&#xff0c;以及正常的点餐&#xff0c;结账和查看账单等等功能 二&#xff1a;实现的功能展示 1.用户登录&#xff08;这里…

[Hei-Ocelot-Gateway ].Net Core Api网关Ocelot的开箱即用版本

&#xfeff;写在前面很多neter都有在用Ocelot做Api网关&#xff0c;但是Ocelot又不像kong或者其他网关一样&#xff0c;开箱即用。它需要你单独开一个web项目来部署&#xff0c;这样很多同学都在做重复的事了。这里[Hei.Ocelot.ApiGateway] 就把这件事给做了&#xff0c;以后有…

聊聊常见的服务(接口)认证授权

&#xfeff;写在前面头发掉得多了&#xff0c;总有机会接触/调到各种各样的接口&#xff0c;各种面向Api编程实际上已经嵌入到我们的习惯中&#xff0c;没办法现在服务端通信还得是http(s)&#xff0c;其他协议还未能成为通用的。大厂的开发平台api我先不敢说&#xff0c;各种…

二分查找(划分时左右元素个数不相等)解析+代码

一:问题描述 当我们在用二分法查找元素的时候&#xff0c;我们往往特希望遇到是奇数个元素个数的数组&#xff0c;因为划分完左右两边的个数相等&#xff0c;所以在以前刚学二分法的时候就有这个疑问&#xff0c;当时就是模模糊糊过去了&#xff0c;再遇到其实还是会有疑问。现…

网络计算机室电源线怎么布,网吧综合布线(电源和网络)经验谈

电源系统布线篇网吧目前所提供的服务&#xff0c;像网页浏览、网络游戏、在线电影、远程教育等最基本的服务都与网络有关&#xff0c;网络质量的好坏直接决定了网吧的生存能力。所以&#xff0c;如何规划一个优质的网络环境&#xff0c;是网吧经营者必须要考虑的一个要点&#…

69. Sqrt(x)010(二分法求解+详解注释)

一&#xff1a;题目 ‘给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 注意&#xff1a;不允许使用任何内置指数函数和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0…

计算机网络 哪个教材好,学习计算机网络哪本教材最好?

benxiuxian高分答主12-29TA获得超过8057个赞地理期末复习计划临近期末&#xff0c;为帮助学生理顺知识&#xff0c;培养学生灵活运用知识分析问题&#xff0c;解决问题的能力&#xff0c;形成完整的知识体系&#xff0c;特作复习计划如下&#xff1a;一、复习目的&#xff1a;1…

安装VSCode作为常用的文本编辑器

作为程序员&#xff0c;跟文本编辑器打交道那是天天都在做的事情&#xff0c;一个趁手的文本编辑器能大大地提供工作效率&#xff0c;减少996福报。笔者使用过各种文本编辑器&#xff0c;或是xx版&#xff0c;或是免费版&#xff0c;多多少少都有一些不便之处&#xff0c;如今 …

35. 搜索插入位置011(二分查找)

一&#xff1a;题目&#xff1a; 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], …

利用 Github Actions 自动更新 docfx 文档

利用 Github Actions 自动更新 docfx 文档Introdocfx 是微软出品一个 .NET API 文档框架&#xff0c;有一个理念是代码即文档&#xff0c;会根据项目代码自动生成 API 文档&#xff0c;即使没有写任何注释也会生成 API 文档&#xff0c;也有一些默认的主题可以配置&#xff0c;…

34. 在排序数组中查找元素的第一个和最后一个位置012(二分查找+思路+详解+两种方法)Come Baby!!!!!!!! !

一&#xff1a;题目 给定一个按照升序排列的整数数组 nums&#xff0c;和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 进阶&#xff1a; 你可以设计并实现时间复杂度为 O(log n) 的算法解…

计算机专业可以报考放射医学吗吗,文科生可以报考医学影像技术专业吗

近期很多文科同学都在疑惑可不可以报考医学影像技术专业&#xff0c;这里有途网小编告诉大家&#xff0c;医学影像技术专业是理科专业&#xff0c;文科生一般来说是不可以报考医学影像技术专业的&#xff0c;但在每年高校招生计划发布的时候也可能会考虑招收文科生的情况&#…

基于PaaS平台的多应用自集成方案之公共数据集成

源宝导读&#xff1a;在明源云“天际”PaaS平台之上&#xff0c;可以构建、部署和运行多个业务应用&#xff0c;并支持多应用分离部署&#xff0c;以提升系统整体的性能和稳定性。本文将介绍多应用自集成解决方案以及相关的实现细节。一、背景1.1、业务场景在明源云“天际”Paa…

可能是Asp.net Core On host、 docker、kubernetes(K8s) 配置读取的最佳实践

&#xfeff;写在前面为了不违反广告法&#xff0c;我竭尽全力&#xff0c;不过“最佳实践”确是标题党无疑&#xff0c;如果硬要说的话 只能是个人最佳实践。问题引出可能很多新手都会遇到同样的问题&#xff1a;我要我的Asp.net Core 应用传统方式直接部署(host)&#xff0c;…

33. 搜索旋转排序数组(013)二分查找+思路详解+来干了这杯代码!!!!!!

一&#xff1a;题目 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], n…