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基类。里面有几个方法:
- BuildRenderTree 用来构建html,css等ui元素
- 其它code部分会也会被合并到这个类里面
生命周期
了解组件声明周期对我们使用组件有很大的帮助。一个组件的声周期主要依次以下几个阶段:
- OnInitialized、OnInitializedAsync
- OnParametersSet、OnParametersSetAsync
- OnAfterRender、OnAfterRenderAsync
- 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
ASP.NET Core Blazor Webassembly 之 组件的更多相关文章
- ASP.NET Core Blazor Webassembly 之 路由
web最精妙的设计就是通过url把多个页面串联起来,并且可以互相跳转.我们开发系统的时候总是需要使用路由来实现页面间的跳转.传统的web开发主要是使用a标签或者是服务端redirect来跳转.那今天来 ...
- ASP.NET Core Blazor Webassembly 之 数据绑定
上一次我们学习了Blazor组件相关的知识(Asp.net Core Blazor Webassembly - 组件).这次继续学习Blazor的数据绑定相关的知识.当代前端框架都离不开数据绑定技术. ...
- [Asp.Net Core] Blazor WebAssembly - 工程向 - 如何在欢迎页面里, 预先加载wasm所需的文件
前言, Blazor Assembly 需要最少 1.9M 的下载量. ( Blazor WebAssembly 船新项目下载量测试 , 仅供参考. ) 随着程序越来越复杂, 引用的东西越来越多, ...
- ASP.NET Core Blazor WebAssembly 之 .NET JavaScript互调
Blazor WebAssembly可以在浏览器上跑C#代码,但是很多时候显然还是需要跟JavaScript打交道.比如操作dom,当然跟angular.vue一样不提倡直接操作dom:比如浏览器的后 ...
- ASP.NET Core Blazor Webassembly 之 渐进式应用(PWA)
Blazor支持渐进式应用开发也就是PWA.使用PWA模式可以使得web应用有原生应用般的体验. 什么是PWA PWA应用是指那些使用指定技术和标准模式来开发的web应用,这将同时赋予它们web应用和 ...
- ASP.NET Core Blazor WebAssembly实现一个简单的TODO List
基于blazor实现的一个简单的TODO List 最近看到一些大佬都开始关注blazor,我也想学习一下.做了一个小的demo,todolist,仅是一个小示例,参考此vue项目的实现http:// ...
- ASP.NET Core Blazor 用Inspinia静态页模板搭建简易后台(实现菜单选中)
Blazor 是一个用于使用 .NET 生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建丰富的交互式 UI. 共享使用 .NET 编写的服务器端和客户端应用逻辑 ...
- ASP.NET Core Blazor 初探之 Blazor Server
上周初步对Blazor WebAssembly进行了初步的探索(ASP.NET Core Blazor 初探之 Blazor WebAssembly).这次来看看Blazor Server该怎么玩. ...
- 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(中)
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 四.创建一个Blazor应用程序 1. 第一种创 ...
随机推荐
- 谈谈JavaScript中的变量、指针和引用
1.变量 我们可能产生这样一个疑问:编程语言中的变量到底是什么意思呢? 事实上,当我们定义了一个变量a时,就是在存储器中指定了一组存储单元,并将这组存储单元命名为a.变量a的值实际上描述的是这组存储单 ...
- 详解如何使用gulp实现项目在浏览器中的自动刷新
情况描述: 我们很容易遇到这样一种情况: 我们并不是一开始就规划好了整个项目,比如可能接手别人的项目或者工程已经手动创建好了,现在要想利用gulp来实现浏览器自动刷新,那么如何做呢? 其实非常简单,本 ...
- U-Mail邮件系统详解邮件收发延迟原因及解决方案
邮件是现代社会办公最常见.最频繁的通联工具,但使用邮件系统时,用户普遍最关心两个安全,一个是安全性,邮件会不会被窃密?自己的邮箱账号会不会被盗取被攻占呢?保存的数据会不会丢失呢?关于这个问题,国内知名 ...
- 网络流--最大流--hlpp(预流推进)模板
//500ms 秒掉洛谷推流问题 #include <algorithm> #include <iostream> #include <cstring> #incl ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之十一(四十七)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- 【Linux基础总结】Shell 基础编程
Shell 基础编程 重启虚拟机遇到磁盘损坏如何解决 Shell编程中变量的声明.引用及作用域 Shell程序 概述 以文件形式存放批量的Linux命令集合,该文件能够被Shell解释执行,这种文件就 ...
- Qt子窗口设置背景色只能应用到其中的部件的问题
问题描述:设置父窗口后子窗口会嵌在父窗口中,背景变透明,此时用qss设置子窗口的背景色发现只应用到的子窗口的控件中,除控件外的地方并没有应用到背景色. 解决方法:不使用qss设置背景色,重写paint ...
- [hdu5439 Aggregated Counting]公式化简,预处理
题意:按下列规则生成一组序列,令f(n)为n这个数在序列中出现的最后一个位置,求f(f(n))的值. 1. First, write down 1, 2 on a paper.2. The 2nd n ...
- python--制作微信好友照片墙
知识来源:https://zhuanlan.zhihu.com/p/73975013 1.环境 os:MAC tool:python 3.7 ,pip3.7 2.前提: 使用pip3.7 instal ...
- java ->IO流_序列化流与反序列化流
序列化流与反序列化流 用于从流中读取对象的操作流 ObjectInputStream 称为 反序列化流 用于向流中写入对象的操作流 ObjectOutputStream 称为 序列化流(对象 ...