https://blazor-university.com/学习笔记。
JavaScript interop
WebAssembly不支持以下API:
- Media Capture
- Popups
- Web GL
- Web Storage
必须通过JS访问以上API。
JS互操作有一些缺陷:
- 不要在预渲染阶段进行
- 不要过早使用ElementReference
- 要废弃资源以避免资源泄漏
- 要避免在废弃的.NET引用上调用方法
- 不要在Blazor还没有初始化的时候调用.NET方法
JavaScript boot process
Blazor的初始化一定是在浏览器加载完资源之后。
所以推荐的做法是由Blazor发起到JS的互操作。
若使用ServerPrerendered,可以看到:
Blazor initialised: 42:22.559
JavaScript initialised: 42:22.631
Blazor initialised: 42:22.690
也就是Blazor初始化了两次。
Calling JavaScript from .NET
过。
Updating the document title
<script src="~/scripts/DocumentInterop.js"></script>
注意一下,竟然可以使用~
在路径中。
对JS的交互最早可以在OnAfterRender中执行,因为OnAfterRender只有在客户端执行的时候firstRender才是true,因而有下面的代码:
@inject IJSRuntime JSRuntime
@code {
[Parameter]
public string Title { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await JSRuntime.InvokeVoidAsync("BlazorUniversity.setDocumentTitle", Title);
}
}
Passing HTML element references
可以用@ref
指示来定义一个ElementReference:
@page "/"
<h1 @ref=MyElementReference>Hello, world!</h1>
Welcome to your new app.
@code {
ElementReference MyElementReference;
}
实际生成的HTML代码形如:
<h1 _bl_bc0f34fa-16bd-4687-a8eb-9e3838b5170d="">Hello, world!</h1>
一个关于autofocus的案例。HTML页面加载完毕之时会聚焦于第一个具有autofoucs属性的HTML元素之上。可是Blazor是SPA,每次变换HTML的时候并不加载,支持修改URL。所以autofocus的行为不会发生。为了在Blazor中解决这个问题。做法是在OnAfterRenderAsync的时候如果是firstRender,则调用标的上的focus方法。
一个改进的方法是使用将此行为提取成一个AutoFocus组件。
在OnAfterRender之前使用ElementReference上的操作是不合适的,因为元素可能只存在于RenderTree,还没打到DOM上。甚至在那之前,ElementReference都是无效的。下面是Blazor的操作流程:
- 生成虚拟的渲染树
- 应用变动到DOM
- 对于每个标记有
@ref
的元素,更新组件内对应的EleementReference成员
Calling .NET From JavaScript
只有特点.NET代码才可以在JS中调用:
- 必须具有
JsInvokable
属性 - 必须为public
- 必须可序列化为JSON
- 必须返回可序列化为JSON的值
- 若
JsInvokable
属性指定有identifier参数,那么参数值必须唯一
然后.NET对象的引用必须传给JS。但是普通的传值需要经过序列化,所以不可以直接采用。而必须通过DotNetObjectReference.create来创建一个引用之后来传给JS。
可通过invokeMethodAsync方法来调用.NET方法。
Lifetimes and memory leaks
创建DotNetObjectReference而不释放清除会导致内存泄漏。为了解决这个问题,要实现DotNetObjectReference的IDisposable方法:
@implements IDisposable
@code {
public void Dispose()
{
GC.SuppressFinalize(this);
if (ObjectReference != null)
{
//Now dispose our object reference so our component can be garbage collected
ObjectReference.Dispose();
}
}
}
GC.SuppressFinalize
待考证- 看起来ObjectReference不随组件的生命周期自动管理,而是属于组件外部资源,需要一些手动管理。
- Dispose会使其从JSRuntime移除
- 假设组件销毁时,由其生成的Web内容也会子自动销毁。
- 这个假设可能不会成立,可能需要移除JS侧对被销毁方法的使用。
Type safety
JS类型于.NET类型之间的对应关系有限:
JS | .NET |
---|---|
boolean | System.Boolean |
string | System.String |
number | System.Float/System.Decimal或System.Int32(若是整型) |
Date | System.DateTime或者System.String |
JS调用.NET时,对于enum,一般情况下需要传整数值,但是经过[System.Text.Json.Serialization.JsonConverter]
修饰之后可以传字符串值,例如:
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
public enum TestEnum
{
FirstValue = 100,
SecondValue = 200
};
对于以上定义,下面两种调用方式皆可:
dotNetObject.invokeMethodAsync('OurInvokableDotNetMethod', 'FirstValue');
dotNetObject.invokeMethodAsync('OurInvokableDotNetMethod', 200);
Calling static .NET methods
在JS侧尚没有方法判断.NET侧是否就绪可以被调用。但是可以通过在JS侧启动Blazor的方式来进行一定的协调。
适合在JS侧调用的.NET静态方法:
- 静态方法必须时public,且其所在的类也必须时public
- 对于同步执行的方法,返回值类型必须是void或者可以序列化成JSON的类型
- 对于异步执行的方法,返回值类型必须是Task或者Task,其中T必须是可以序列化成JSON的
- 所有的入参必须可以序列化成JSON
- 方法必须标注有
[JSInvokable]
,若在[JSInvokable]
中指定了identifier,其内容不能重复
一个示例:
setTimeout(async function () {
const settings = await DotNet.invokeMethodAsync("CallingStaticDotNetMethods", "GetSettings");
alert('API key: ' + settings.someApiKey);
}, 1000);
(本篇完)