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库需要初始化。

(本篇完)