Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

05 Aug 2021

Blazor University学习笔记【二】

https://blazor-university.com/学习笔记。

Components

续上篇。

Directives

Blazor尚不允许创建自定义的指示(Directive)

由于C#是强类型,一个指示知道自己的作用对象,以及接受的值类型,不需要使用@来额外标出表达式。一个例外是lambda:@onclick=@( args => Debug.WriteLine("Clicked") )

下面的例子展示@onclick事件是如何被挂载并处理的:

// Razor mark-up with @ref directive
<h1 @onclick=H1Clicked>Hello, world!</h1>

@code
{
  public void H1Clicked(MouseEventArgs args)
  {
    System.Diagnostics.Debug.WriteLine("H1 clicked");
  }
}

// Transpiled C#
public partial class Index : Microsoft.AspNetCore.Components.ComponentBase
{
  protected override void BuildRenderTree(RenderTreeBuilder __builder)
  {
    __builder.OpenElement(0, "h1");
    __builder.AddAttribute(1, "onclick", EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this, H1Clicked));
    __builder.AddContent(2, "Hello, world!");
    __builder.CloseElement();
  }
}

一些标准指示:

  • @code,用于放置C#代码,可以有多个此部块,最终输出的时候这些部块会被整合起来。
  • @page,用于在转译的类中生成一个[PageAttribute],用于支持路由。
  • @layout,在转译的类中生成一个[LayoutAttribute],用于指定布局。
  • @typeparam,指示Blazor从Razor的标签代码生成一个通用类
  • @inject,允许组件指定所需的依赖,但此组件被创建的时候,这些依赖会被注入
    • 大概是因为组件的生命周期归框架管理,所以不能直接引入这些依赖,而是要请求注入
  • @attribute,添加指定的DotNet属性

下列指令即可用于组件又可用于HTML元素:

  • @ref,若当前组件的成员或者辖属需要持有到子组件或者HTML元素的引用,需要以此指示给出。这些可以JS互操作的时候作为引用,或者让当前组件用于子组件的引用,以便调用子组件的方法。
  • @bind,允许(双向)数据绑定
  • @attributes,输出键值对作为HTML的属性
  • @key,给元素或者组件加上唯一标识

下面是HTML DOM元素事件的子集,可用于组件事件处理:

  • @onmousemove
  • @onclick
  • @onkeypress
  • @onscroll

指示可以带有属性:

 <div @onclick=SecondLevelClicked @onclick:stopPropagation>

上面的@onclick带有属性stopPropagation可以阻止事件进一步向上冒泡。一些属性可以带有值:@directive:attribute="value"

Component events

事件回调声明的形式是这样的:

[Parameter]
public EventCallback<int> OnMultipleOfThree { get; set; }

事件的触发形式是这样的:

await OnMultipleOfThree.InvokeAsync(currentCount)

EventCallback<T>.InvokeAsync返回的是一个Task,可以在上面执行await操作。

事件处理的形式是这样的:

@page "/"

Last multiple of three = @LastMultipleOfThree

<Counter OnMultipleOfThree=@UpdateLastMultipleOfThreeValue/>

@code
{
  int LastMultipleOfThree = 0;

  private void UpdateLastMultipleOfThreeValue(int value)
  {
      LastMultipleOfThree = value;
  }
}

EventCallback<T>与.NET 事件的差别

EventCallback<T>是Single-case,而.NET事件是Multi-case。

也就是说EventCallback<T>只能有一个回调。

.NET事件的回调是一个类型,而Blazor事件的回调时readonly struct,且不能为null。

.NET事件是同步的,Blazor事件是异步的。

.NET事件不能作为[Parameter]

Blazor事件回调触发后,会自动执行StateHaschanged()来刷新回调所在的组件。.NET事件(例如Action<T>)则不具备此功能。

@code {
  [Parameter]
  public Action<int> OnMultipleOfTwoAction { get; set; }

  private async Task IncrementCount()
  {
    if (currentCount % 2 == 0)
      OnMultipleOfTwoAction?.Invoke(currentCount);
  }
}
<ul>
  <li>Last multiple of two = @LastMultipleOfTwo</li>
</ul>

<Counter OnMultipleOfThree=@UpdateLastMultipleOfThreeValue OnMultipleOfTwoAction=@UpdateLastMultipleOfTwoValue />

@code
{
  int LastMultipleOfTwo = 0;

  private void UpdateLastMultipleOfTwoValue(int value)
  {
    LastMultipleOfTwo = value;
  }
}

上面代码中的Action<int>所定义的事件不会自动刷新回调所在组件。

EventCallback<T>回调的方法形如private Task SomeName(T value),无所谓其可见度是private还是其他,却可以是异步的也可以是同步的。

异步的例子:

public Task SomethingHappenedInChildComponent(string value)
{
  // Do something with value
  return Task.CompletedTask;
}

同步的例子:

public void SomethingHappenedInChildComponent(string value)
{
  // Do something with value
}

如果不关心参数,那么参数可以省略:

// Either
public void SomethingHappenedInChildComponent()
{
  // Do something that doesn't need the value
}

// Or
public Task SomethingHappenedInChildComponent()
{
  // Do some asynchronous work that doesn't need the value
  return SomeTask;
}

Browser DOM events

可以用C#来处理HTML元素的DOM事件。事件处理器要么是字符串,要么是Microsoft.AspNetCore.Components.Web.ProgressEventArgs类型。

像onmousemove这种事件可能会触发很多次,对于Server端,会导致过载。

JS调用C#方法的时候是异步的,所以事件无法像在JavaScript里面那样被Cancel。

可用的DOM事件,以及其参数类型:

  • General Events (参数类型为EventArgs)
    • onactivate
    • onbeforeactivate
    • onbeforedeactivate
    • ondeactivate
    • onended
    • onfullscreenchange
    • onfullscreenerror
    • onloadeddata
    • onloadedmetadata
    • onpointerlockchange
    • onpointerlockerror
    • onreadystatechange
    • onscroll
  • Focus Events (参数类型FocusEventArgs)
    • onfocus
    • onblur
    • onfocusin
    • onfocusout
  • Mouse Events (参数类型为MouseEventArgs)
    • onmouseover
    • onmouseout
    • onmousemove
    • onmousedown
    • onmouseup
    • onclick
    • ondblclick
    • oncontextmenu
    • onwheel
    • onmousewheel
  • Drag Events (参数类型为DraEventArgs)
    • ondrag
    • ondragend
    • ondragenter
    • ondragleave
    • ondragover
    • ondragstart
    • ondrop
  • Keyboard Events (参数类型为keyboardEventArgs)
    • onkeydown
    • onkeyup
    • onkeypress
  • Input Events (参数类型为ChangeEventArgs或者EventArgs)
    • onchange
    • oninput
    • oninvalid
    • onreset
    • onselect
    • onselectstart
    • onselectionchange
    • onsubmit
  • Clipboard Events (参数类型为EventArgs或者ClipboardEventArgs)
    • onbeforecopy
    • onbeforecut
    • onbeforepaste
    • oncopy
    • oncut
    • onpaste
  • Touch Events (参数类型为TouchEventArgs)
    • ontouchcancel
    • ontouchend
    • ontouchmove
    • ontouchstart
    • ontouchenter
    • ontouchleave
  • Pointer Events(参数类型为PointerEventArgs)
    • ongotpointercapture
    • onlostpointercapture
    • onpointercancel
    • onpointerdown
    • onpointerenter
    • onpointerleave
    • onpointerout
    • onpointerover
    • onpointerup
  • Media Events(参数类型为EventArgs)
    • oncanplay
    • oncanplaythrough
    • oncuechange
    • ondurationchange
    • onemptied
    • onpause
    • onplay
    • onplaying
    • onratechange
    • onseeked
    • onseeking
    • onstalled
    • onstop
    • onsuspend
    • ontimeupdate
    • onvolumechange
    • onwaiting
  • Progress Events(参数类型ProgressEventArgs)
    • onloadstart
    • ontimeout
    • onabort
    • onload
    • onloadend
    • onprogress
    • onerror

(本篇完)

Categories

Tags