https://blazor-university.com/学习笔记。
Components
续上篇。
Multi-threaded rendering
BlazorServer是需要考虑多线程的。
相关代码:
<p>Async rendered by thread @IdOfRenderingThread</p>
@code
{
int IdOfRenderingThread;
protected override async Task OnInitializedAsync()
{
// Runs synchronously as there is no code in base.OnInitialized(),
// so the same thread is used
await base.OnInitializedAsync().ConfigureAwait(false);
IdOfRenderingThread =
System.Threading.Thread.CurrentThread.ManagedThreadId;
// Awaiting will schedule a job for later, and we will be assigned
// whichever worker thread is next available
await Task.Delay(1000).ConfigureAwait(false);
IdOfRenderingThread =
System.Threading.Thread.CurrentThread.ManagedThreadId;
}
}
暂不知道ConfigureAwait是啥意思。
Thread safety using InvokeAsync
UI事件是同步为单线程处理的。
但是非UI事件则需要开发者自行同步。非UI事件包括:
- System.Threading.Timer到期事件
- 被多个使用者所共享的独现例上的事件触发
- 零另一个服务器推送过来的数据
框架方法StateHasChanged只能供单个线程访问,否则会抛出异常:System.InvalidOperationException: The current thread is not associated with the Dispatcher。
server状态,每个浏览器标签对应一个Dispatcher。InvokeAsync就是在此Dispatcher上调用方法。
总得来说,跟写其他UI程序差不多,UI组件只能单线程访问。
文中的例子是这样的:
- 定义一个静态变量Counter.Value
- 创建一个组件IncrementCounter
- OnInitialized的时候开启一个线程。此线程在信号到了时,用于增加Counter.Value,一共增加1000次
- 信号是一个System.Threading.ManualResetEvent,通过组件参数传入
- 同时传入的另一个组件参数是ShouldUseInvokeAsync,此值为真时在Dispatcher递增Counter.Value,否则在本线程递增
- 在根页面创建5个IncrementCounter,传入ShouldUseInvokeAsync以及System.Threading.ManualResetEvent,选择一个统一的时间点开始处理事件,并且等待1秒钟,让所有的IncrementCounter完成工作。
Render trees
Blazor通过BuildRenderTree来构建一个Virtual DOM:
- HTML的属性可以在Virtual DOM中更新多次,而不影响真实的DOM
- Render trees之间易于比对差分
Incremental RenderTree proof
过。
Optimising using @key
贴士:对于在运行时通过循环产生的组件,最好统统都用@key
标识
Blazor在应用差异的时候,默认并不会考虑目标是否已经存在,而仅是对比HTML元素,看是否具有相同的内容。在排序的情况下,这可能导致过多的增删。通过@key
告知Blazor目标已存在,Blazor就会沿用已存在的HTML元素,避免不必要的增删。
@key
可以是任意类型的对象,但最好其变化特性和目标的HTML元素一致。
Component libraries
组件库允许将组件以及页面打包成可以重复使用的软件库,连同其支持文件,比如CSS,JavaScript以及图像文件。
在Visual Studio中创建一个Razor Class Library工程。
支持文件只要放在wwwroot目录下即可。在组件中如何访问这些支持文件呢,需要采用形如/_content/PackageId/MyImage.png
的格式,其中:
- _content是所有支持文件的根目录
- PackageId是包含这些资源的二进制的包标识符。右击class library,选属性,然后选Package页面就能看到。如果是NuGet包,那么包名即代表PackageId。
- MyImage.png,对应具体资源的名字
消费组件库的话,要么添加引用到目标工程的引用,要么添加引用到NuGet库。
只是需要注意以下,有些NuGet库需要初始化。
(本篇完)