Blazor带我重玩前端(六)
本文主要讨论Blazor事件内容,由于blazor事件部分很多,所以会分成上下两篇,本文为第二篇。
双向绑定
概述
如图所示
- 当点击单项绑定的时候,MyOnewayComponent里的属性值会发生变化,这种变化是单项的,仅仅只是本地副本的值的变化,并不会引发父页面的值发生变化。但当点击父页面的Click Me的时候,会修改MyOnewayComponent的属性值会被修改。所以单项绑定强调的是占位,以达到动态输出的目的。
- 当点击双向绑定的时候,三个值会同步发生变化。即便点击父页面的Click Me,也不会覆盖掉MyTwoWayComponent的属性值,这说明父页面和MyTwoWayComponent页面的值发生了双向绑定,会导致数据同步变化。
- 双向绑定,绑定的是Blazor组件和dom元素,就像是宏指令一样。也就是说,当该组件首次运行时,输入框的值来自于CurrentValue属性,当用户输入新的值后,CurrentValue也将会被设置成新的值。
示例
双向绑定有一个重要特征就是使用@bind-进行数据绑定,之前我创建了两个组件,我们来看一下这两个组件的源代码:MyOnewayComponent:
1: <div>
2: MyComponent CounterValue is @CounterValue
3: </div>
4: <button @onclick=UpdateCounterValue>Update</button>
5: @code {
6:
7: [Parameter]
8: public int CounterValue { get; set; }
9:
10: void UpdateCounterValue()
11: {
12: CounterValue++;
13: }
14: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
MyTwoWayComponent:
1: <div>
2: MyComponent CounterValue is @CounterValue
3: </div>
4: <button @onclick=UpdateCounterValue>Update</button>
5: @code {
6:
7: [Parameter]
8: public int CounterValue { get; set; }
9:
10: [Parameter]
11: public EventCallback<int> CounterValueChanged { get; set; }
12:
13: async Task UpdateCounterValue()
14: {
15: CounterValue++;
16: await CounterValueChanged.InvokeAsync(CounterValue);
17: }
18: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
以上代码可以看到有明显的不同,MyTwoWayComponent包含一个EventCallback类型的属性,其命名是CounterValueChanged,看起来像是属性值后缀Changed,其调用方法也变成了async Task,该方法表明,当CounterValue发生变化的时候,会通过CounterValueChanged来通知事件源页面该值发生了变化。额外尝试一下,如果我们直接使用MyOnewayComponent 来演示双向绑定,会发生什么,我们使用如下代码运行一下看看:
1: <MyOnewayComponent @bind-CounterValue="@currentCount" />
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
运行后,发现报错了,错误信息是:Unhandled exception rendering component: Object of type 'BlazorApp.Client.Pages.MyOnewayComponent' does not have a property matching the name 'CounterValueChanged'。由此可见,我们的命名规则是强制的,其必须是所绑定EventCallBack的属性名后缀Changed。
BuildTree源码
1: #pragma warning disable 1998
2: protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
3: {
4: __builder.AddContent(8, "Click me");
5: __builder.CloseElement();
6: __builder.AddMarkupContent(9, "\r\n<br>\r\n<br>\r\n\r\n");
7: __builder.OpenComponent<BlazorApp.Client.Pages.MyOnewayComponent>(10);
8: __builder.AddAttribute(11, "CounterValue", Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<System.Int32>(
9: #nullable restore
10: currentCount
11:
12: #line default
13: #line hidden
14: #nullable disable
15: ));
16: __builder.AddAttribute(12, "CounterValueChanged", Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback(this, __value => currentCount = __value, currentCount));
17: __builder.CloseComponent();
18: }.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
- 8-15行是单项绑定的内容
- 16-18行是双向绑定的内容
级联值和参数
概述
级联值和参数是一种将值从组件传递到其所有子组件的方法,在Blazor中,采用CascadingValue来实现,子组件通过声明同一类型的属性(用[CascadingParameter]属性修饰)来收集并赋值。当级联值发生更新的时候,这种更新将传递到所有的子组件,同时这组件将会自动调用StateHasChanged 。一般情况下,我们的CascadingValue中可能会需要传递多个值的变化,那么这种变化是如何进行的呢。是通过两种方式,一种是类型推导,一种是命名传值。
类型推导
我创建了两个组件,分别是FirstComponent,SecondComponent。FirstComponent源码如下:
1: <CascadingValue Value="@CascadingIndex">
2: <CascadingValue Value="@CascadingName">
3: <SecondComponent></SecondComponent>
4: </CascadingValue>
5: </CascadingValue>
6: @code {
7: int CascadingIndex = 10000;
8:
9: string CascadingName = "FirstComponent";
10: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
SecondComponent源码如下:
1:
2: <h3>SecondComponent</h3>
3: <p>
4: CascadingValue Is <strong>@SecondValue</strong>
5: </p>
6: @code {
7: [CascadingParameter] int SecondValue { get; set; }
8: }
传值的过程中,我们只有一个int类型的属性,所以该值会显示10000,如下图所示:

如果我们修改一下FirstComponent的源码,将其中的string类型的属性删除掉,同时增加一个新的int类型的属性,如下源码所示:
1: <CascadingValue Value="@CascadingIndex">
2: <CascadingValue Value="@Total">
3: <SecondComponent></SecondComponent>
4: </CascadingValue>
5: </CascadingValue>
6:
7: @code {
8: int CascadingIndex { get; set; } = 10000;
9:
10: int Total{ get; set; } = 2;
11: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
运行结果如下:

由此可见,当子组件遇到多个相同类型的属性的时候,会选择离子组件最近的属性的值并传递到自己的属性中去。
命名传值
命名赋值就很单纯了,主要考虑绑定正确的名称就行。修改后FirstComponent的源码如下,需要指定Name:
1: <CascadingValue Value="@CascadingIndex" Name="CascadingIndex">
2: <CascadingValue Value="@Total" Name="Total">
3: <SecondComponent></SecondComponent>
4: </CascadingValue>
5: </CascadingValue>
6:
7: @code {
8: int CascadingIndex{ get; set; } = 10000;
9:
10: int Total{ get; set; } = 2;
11: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
SecondComponent源码如下,可以指定名称已接收值的传递
1: <h3>SecondComponent</h3>
2: <p>
3: CascadingValue Is <strong>@SecondValue</strong>
4: </p>
5: @code {
6: [CascadingParameter(Name = "CascadingIndex")] int SecondValue { get; set; }
7: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
运行后的结果如下,值又变回了一万

有朋友可能会想,我不想设置SecondComponent中CascadingParameter的Name值,但是我可以设置成FirstComponent中某个已经绑定的Name的名称。如下所示:
FirstComponent源码不变,SecondComponent源码如下:
1: <h3>SecondComponent</h3>
2: <p>
3: CascadingValue Is <strong>@Total</strong>
4: </p>
5: @code {
6: [CascadingParameter] int Total { get; set; }
7: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
运行结果如下所示:
由此可见,不设置子组件中CascadingParameter的Name值,是无法接收传递的值的。
性能问题
默认情况下,Blazor会持续监控级联值的变化,并将其传递到所有子组件中,这将会占用一定的资源,并可能导致性能问题。
如果我们可以确定,我们的级联值不会发生变化,可以设置CascadingValue中参数IsFixed的值为true,这样的Blazor就不会监控级联值的变化了。
1: <CascadingValue Value="@CascadingIndex" IsFixed="true">
2: <SecondComponent></SecondComponent>
3: </CascadingValue>
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Blazor带我重玩前端(六)的更多相关文章
- Blazor带我重玩前端(一)
写在前面 曾经我和前端朋友聊天的时候,我说我希望有一天可以用C#写前端,不过当时更多的是美好的想象,而现在这一切正变得真实…… 什么是Blazor 我们知道浏览器可以正确解释并执行JavaScript ...
- Blazor带我重玩前端(三)
写在前面 需要升级VS2019以及.NET Core到最新版(具体的最低支持,我已经忘了,总是越新支持的就越好),以更好的支持自己开发Blazor项目. WebAssembly 搜索Blazor模板 ...
- Blazor带我重玩前端(四)
布局 Blazor中的布局和MVC中的布局是类似的. 创建布局 新建一个Razor页面,所有新增的布局都要继承LayoutComponentBase,同时标识自定义内容的输出位置,即标识Body的位置 ...
- Blazor带我重玩前端(五)
概述 本文主要讨论Blazor事件内容,由于blazor事件部分很多,所以会分成上下两篇,本文为第一篇,后续会有第二篇. 我们可以视组件是一个类,我们先看一下前文所说的Index.Razor页面生成的 ...
- Blazor带我重玩前端(二)
概览 Blazor目前有两种托管模式,一种是Server-Side模式,一种是WebAssembly模式.官方首先支持的是Service-Side模式,使用WebAssembly模式,需要更新到最新版 ...
- 重学前端 --- Promise里的代码为什么比setTimeout先执行?
首先通过一段代码进入讨论的主题 var r = new Promise(function(resolve, reject){ console.log("a"); resolve() ...
- 重学前端--js是面向对象还是基于对象?
重学前端-面向对象 跟着winter老师一起,重新认识前端的知识框架 js面向对象或基于对象编程 以前感觉这两个在本质上没有什么区别,面向对象和基于对象都是对一个抽象的对象拥有一系列的行为和状态,本质 ...
- node十年心酸史,带你了解大前端的由来!
前言 近年来,随着前端的丰富,前后端分离是趋势.各种东西如雨后春笋一般,层出不穷.node.js的出现,使前端真正意义上变成了大前端. 前端由来之HTML发展史 1990 年,Tim Berners- ...
- Datatables带参重绘
研究了好久,最后发现只需要加上参数("bDestory":true,) 即可实现每次刷新就是新的重绘,而无需调用什么desctory init clear等等函数..
随机推荐
- 2019 7 8 HL 模拟赛
今天 很不爽 昨天晚上没有睡好觉 大约2点才睡着吧 反正翻来覆去睡不着 不知道为什么可能可行流 或者可行费用流并没有深刻理解 .我不会写 让我心情非常的焦躁. 大凶 顺理成章的被3位强者吊着锤(妈呀我 ...
- Springboot中的CommandLineRunner
CommandLineRunner接口的作用 在平常开发中可能需要实现在启动后执行的功能,Springboot提供了一种简单的实现方案,即实现CommandLineRunner接口,实现功能的代码在接 ...
- 【源码】Python3使用Requests抓取和检测电光代理API,并查询ip代理是否成功
电光代理成立后,做一篇笔记,记录我使用Requests抓取和测试电光代理的方法 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做 ...
- C#LeetCode刷题之#589-N叉树的前序遍历(N-ary Tree Preorder Traversal)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4090 访问. 给定一个 N 叉树,返回其节点值的前序遍历. 例如 ...
- C#LeetCode刷题之#561-数组拆分 I(Array Partition I)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3718 访问. 给定长度为 2n 的数组, 你的任务是将这些数分成 ...
- [C/C++]快速读入代码(快读)
快读 1.为什么要有快读 好吧,有些题目看上去十分简单,例如https://www.luogu.com.cn/problem/P4305这道题,实际上数据量巨多,光是一个测试点就可能有几个MB,在这种 ...
- python的一些使用体会
python刚开始接触,因为刚好有点需求,所以写了点小程序,一点点体会. 优点: 1. os.rename()方法不错,c#就没有这个方法 2.字符串的slice操作不错,取substring有时比较 ...
- Linux命令--vi/vim复制一行,粘贴
在光标的位置按“yy”,复制当前行: 然后再光标的行按“p”,粘贴到下一行,原来的往下顺移
- java反序列化——XMLDecoder反序列化漏洞
本文首发于“合天智汇”公众号 作者:Fortheone 前言 最近学习java反序列化学到了weblogic部分,weblogic之前的两个反序列化漏洞不涉及T3协议之类的,只是涉及到了XMLDeco ...
- 《Python 测试开发技术栈—巴哥职场进化记》—每日站会的意义
上文<Python测试开发技术栈-巴哥职场进化记>-一道作业题我们讲到华哥给巴哥出了一道作业题,让巴哥用Python实现记录日志的功能,巴哥历经"千辛万苦",终于做出了 ...
