https://blazor-university.com/学习笔记。
Components
续上篇。
Code generated HTML attributes
无法在<>
内添加C#的控制语句。
像下面这种也不行:
<div @IfYouCanSeeThisTextThenTheCodeWasNotExecutedHere />
Razor只在下面的几处执行C#代码:
- 在元素的内容区域,如
<span>@GetSomeHtml()</span>
- 在元素的属性赋值区域,如
<img src=@GetTheImageForTheUrl() />
- 在
@code
块部
若要编码生成属性键值对,需要采用一种叫做Attribute splattin
的方式,也就是将一个Dictionary<string, object>
现例赋值给元素的@attributes
:
<div @attributes=MyCodeGeneratedAttributes/>
@code
{
Dictionary<string, object> MyCodeGeneratedAttributes;
protected override void OnInitialized()
{
MyCodeGeneratedAttributes = new Dictionary<string, object>();
for(int index = 1; index <= 5; index++)
{
MyCodeGeneratedAttributes["attribute_" + index] = index;
}
}
}
上述代码的输出结果为:
<div attribute_1="1" attribute_2="2" attribute_3="3" attribute_4="4" attribute_5="5"></div>
对于HTML元素中那些默认不带有值的属性,只要其有值就会被激活。下面的例子中readonly和disabled的值为false,但是readonly和disabled会被激活:
<input readonly="false" disbabled="false"/>
在Blazor中,如果:
<input readonly=@true disabled=@false/>
则上面的disabled不会出现在结果中。
Capturing unexpected parameters
接收键值对参数的方式跟attribute splatting类似,都涉及一个Dictionary<string, object>
现例,以及@attributes
:
<div class="row" role="img" aria-label=@alt>
<img src=@src @attributes=AllOtherAttributes />
</div>
@code
{
[Parameter]
public string src { get; set; }
[Parameter]
public string alt { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> AllOtherAttributes { get; set; }
}
Replacing attributes on child components
可以指定参数合集的默认值:
<div first="1" second="2" third="3" fourth="4" @attributes=AllOtherAttributes>
Right-click and inspect the HTML for this element to see the results!
</div>
若传入的是:
<ChildComponent first="consumer-value-1" second="consumer-value-2" />
则会生成:
<div first="consumer-value-1" second="consumer-value-2" third="3" fourth="4">
Right-click and inspect the HTML for this element to see the results!
</div>
如果把默认值指定在参数合集之后,则能避免其被修改。下面的例子中,为了避免type="number"
被修改,但是允许class="form-control"
被修改,需要这么写:
<input class="form-control" @attributes=AllOtherAttributes type="number"/>
简单地说,参数合集是Inserted,在其之前是Replacable的参数,在其之后是Protected参数,合称 R.I.P.。
Component lifecycles
Blazor组件基类提供了许多虚函数,供派生类覆改。
Parent renders ----------> Create instance if required
|
+ ------> SetParametersAsync
|
v
OnInitialized / OnInitializedAsync [x]
|
v
OnParameterSet / OnParamterSetAsync
|
+--------> StateHasChanged <----- Events
|
v
ShouldRender [x]
| true
v
BuildRenderTree ----+
|
v
Create added child components
|
v
Dispose removed child components
|
OnAfterRender / OnAfterRenderAsync <---+
Parent removed from render tree ------> IDisposable Dispose -------> Dispose child components
- SetParametersAsync, 只要上级节点渲染,此方法必备调用
- 入参会被装置在一个ParameterView中。
- 这是个发起与服务端异步沟通的好时机。
- 在覆改中调用
base.SetParametersAsync
会设置[Parameter]
辖属 - 也是设置默认参数值的时机。
- OnInitialized / OnInitializedAsync
- ParameterCollection初始化之后,这些个方法会被执行。
- 和SetParametersAsync不同之处在于:此时的组件状态已经初始化了。
- 但是,此方法只在组件首次创建的时候初始化一次。
- 如果是
@page
,并且浏览去往的是同一URL,那么Blazor不会嗲用IDisposable.Dispose,也不会执行OnInitialized
- OnParametersSet / OnParametersSetAsync
- 首次例现化的时候,会跟在OnInitializedAsync之后执行。
- 后续会跟在SetParametersAsync之后执行
- StateHasChanged
- 告知Blazor需要刷新了
- 在异步方法中告知Blazor进行半途刷新
- ShouldRender
- 如若返回false,会阻止重新计算组件的RenderTree,减少非必要的刷新
- 首次例现化的时候不会执行
- BuildRenderTree
- 在内存中构建一颗渲染树
- 为了加快渲染速度,应该在循环中尽可能使用key指示
- OnAfterRender / OnAfterRenderAsync
- 每次组件刷新后,都会执行
- 刷新的触发时间
- 上级组件刷新
- 用户交互事件
- StateChanged被调用
- 会传入一个参数firstRender告知是否是首次渲染
- 只有在OnAfterRender执行后,访问其他组件的
@ref
才是安全的 - 只有在firstRender为真的OnAfterRender执行后,访问其他元素的
@ref
才是安全的
- Dispose
- 不是ComponentBase的生命周期方法,但是是.NET对象的
- 调用时机,组件从上级的渲染树中移除,且组件实现了IDisposable(
@implements IDisposable
),具体的方法是void IDisposable.Dispose()
值得注意的是,Blazor并不会等耗时的异步操作完成(例如从远端获取数据),而是尽可能的时候触发渲染。
- SetParametersAsync
- await前的动作
- 继续处理生命周期(调用
OnInitialized*
或者OnParameterSet*
)
- 继续处理生命周期(调用
- 退出时的动作
- 无后续动作
- base.SetParametersAsync必须在await之前执行,否则会触发InvalidOperationException异常
- await前的动作
- OnInitializedAsync
- await前的动作
- 渲染此组件
- 退出时的动作
- 继续处理生命周期
- await前的动作
- OnParametersSetAsync
- await前的动作
- 渲染此组件
- 退出时的动作
- 继续处理生命周期
- await前的动作
- OnAfterRenderAsync
- await前的动作
- 无前序动作
- 退出时的动作
- 无后续动作
- await前的动作
简单地说,SetParametersAsync是唯一的不会阻断命周进程的异步操作。
OnAfterRenderAsync看似没有做额外工作,但它本身是执行链条的终点。如果需要在其await前触发渲染,需要写代码显示调用StateHasChanged,否则在await进行OnAfterRenderAsync会导致无限循环。
异步await的命周图:
Parent renders ---------------------------------> SetParametersAsync
OnInitializedAsync <---------------------- await Task1
+------ await Task3 await Task2
| await Task4 Other code
| Other code
| |
| |
| |
| v
| OnParametersSetAsync
| await Task5
| await Task6
| Other code
| |
| |
| |
| v
+-----> StateHasChanged
|
|
v
ShouldRender
true |
|
v
BuildRenderTree -------> Create added child components
|
v
OnAfterRenderAsync <------- Dispose removed child components
await Task7
await Task8
Other code
只有首次await,Blazor才会刷新渲染,后续的await需要手动添加StateHasChanged才能触发渲染:
protected override async Task OnParametersSetAsync()
{
// Automatically renders when next line starts to await
await Task.Delay(1000);
// Explicitly render when next line starts to await
StateHasChanged();
await Task.Delay(1000);
// Explicitly render when next line starts to await
StateHasChanged();
await Task.Delay(1000);
}
(本篇完)