注释
此版本不是本文的最新版本。 要查看当前版本,请参阅本文的.NET 9 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 要查看当前版本,请参阅本文的.NET 9 版本。
.NET 和 JavaScript 之间的调用需要额外的开销,因为:
- 调用是异步的。
- 参数和返回值是 JSON 序列化的,用于在 .NET 和 JavaScript 类型之间提供易于理解的转换机制。
此外,对于服务器端 Blazor 应用,这些调用将通过网络传递。
避免过度细化的调用
由于每个调用涉及一些开销,因此减少调用次数可能会提高效率。 请考虑以下代码,该代码将项集合存储在浏览器的 localStorage
中。
private async Task StoreAllInLocalStorage(IEnumerable<TodoItem> items)
{
foreach (var item in items)
{
await JS.InvokeVoidAsync("localStorage.setItem", item.Id,
JsonSerializer.Serialize(item));
}
}
前面的示例为每个项目发出单独的 JS 互操作调用。 相反,以下方法可将JS互操作简化为一次调用:
private async Task StoreAllInLocalStorage(IEnumerable<TodoItem> items)
{
await JS.InvokeVoidAsync("storeAllInLocalStorage", items);
}
相应的 JavaScript 函数将整个项集合存储在客户端上:
function storeAllInLocalStorage(items) {
items.forEach(item => {
localStorage.setItem(item.id, JSON.stringify(item));
});
}
对于 Blazor WebAssembly 应用,将单个 JS 互作调用滚动到单个调用通常只会在组件进行大量 JS 互作调用时显著提高性能。
考虑使用同步调用
从 .NET 调用 JavaScript
本部分仅适用于客户端组件。
JS 互操作调用是异步的,无论调用的代码是同步还是异步。 调用是异步的,以确保组件在服务器端和客户端呈现模式之间都兼容。 在服务器上,所有 JS 互操作调用都必须是异步的,因为它们通过网络连接发送。
如果你确定组件只在 WebAssembly 上运行,则可以选择执行同步 JS 互操作调用。 这比进行异步调用的开销略少,并且可能会导致呈现周期更少,因为在等待结果时没有中间状态。
若要在客户端组件中进行从 .NET 到 JavaScript 的同步调用,请将 IJSRuntime 强制转换为 IJSInProcessRuntime 以进行 JS 互操作调用:
@inject IJSRuntime JS
...
@code {
protected override void HandleSomeEvent()
{
var jsInProcess = (IJSInProcessRuntime)JS;
var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
}
}
在 .NET 5 或更高版本的客户端组件中使用IJSObjectReference时,可以改为同步使用IJSInProcessObjectReference。 IJSInProcessObjectReference 实现 IAsyncDisposable/IDisposable ,应当被销毁以便进行垃圾回收,从而防止内存泄漏,如下例所示:
@inject IJSRuntime JS
@implements IDisposable
...
@code {
...
private IJSInProcessObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var jsInProcess = (IJSInProcessRuntime)JS;
module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import",
"./scripts.js");
var value = module.Invoke<string>("javascriptFunctionIdentifier");
}
}
...
void IDisposable.Dispose()
{
if (module is not null)
{
await module.Dispose();
}
}
}
在前面的示例中,JSDisconnectedException 在模块处置期间没有被捕获,因为 Blazor 应用中没有 SignalR-Blazor WebAssembly 线路会丢失。 有关详细信息,请参阅 ASP.NET Core BlazorJavaScript 互操作性(JS 互操作)。
从 JavaScript 调用 .NET
本部分仅适用于客户端组件。
JS 互操作调用是异步的,无论调用的代码是同步还是异步。 调用是异步的,以确保组件在服务器端和客户端呈现模式之间都兼容。 在服务器上,所有 JS 互操作调用都必须是异步的,因为它们通过网络连接发送。
如果你确定组件只在 WebAssembly 上运行,则可以选择执行同步 JS 互操作调用。 这比进行异步调用的开销略少,并且可能会导致呈现周期更少,因为在等待结果时没有中间状态。
若要在客户端组件中从 JavaScript 同步调用 .NET,请使用 DotNet.invokeMethod
而不是 DotNet.invokeMethodAsync
。
如果同步调用有效,以下条件需要满足:
本部分仅适用于客户端组件。
JS 互操作调用是异步的,无论调用的代码是同步还是异步。 调用是异步的,以确保组件在服务器端和客户端呈现模式之间都兼容。 在服务器上,所有 JS 互操作调用都必须是异步的,因为它们通过网络连接发送。
如果你确定组件只在 WebAssembly 上运行,则可以选择执行同步 JS 互操作调用。 这比进行异步调用的开销略少,并且可能会导致呈现周期更少,因为在等待结果时没有中间状态。
若要在客户端组件中进行从 .NET 到 JavaScript 的同步调用,请将 IJSRuntime 强制转换为 IJSInProcessRuntime 以进行 JS 互操作调用:
@inject IJSRuntime JS
...
@code {
protected override void HandleSomeEvent()
{
var jsInProcess = (IJSInProcessRuntime)JS;
var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
}
}
在 .NET 5 或更高版本的客户端组件中使用IJSObjectReference时,可以改为同步使用IJSInProcessObjectReference。 IJSInProcessObjectReference 实现 IAsyncDisposable/IDisposable ,应当被销毁以便进行垃圾回收,从而防止内存泄漏,如下例所示:
@inject IJSRuntime JS
@implements IDisposable
...
@code {
...
private IJSInProcessObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var jsInProcess = (IJSInProcessRuntime)JS;
module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import",
"./scripts.js");
var value = module.Invoke<string>("javascriptFunctionIdentifier");
}
}
...
void IDisposable.Dispose()
{
if (module is not null)
{
await module.Dispose();
}
}
}
在前面的示例中,JSDisconnectedException 在模块处置期间没有被捕获,因为 Blazor 应用中没有 SignalR-Blazor WebAssembly 线路会丢失。 有关详细信息,请参阅 ASP.NET Core BlazorJavaScript 互操作性(JS 互操作)。
考虑使用未封送的调用
本部分仅适用于 Blazor WebAssembly 应用。
当在 Blazor WebAssembly 上运行时,可以从 .NET 进行到 JavaScript 的非封装调用。 这些是不对参数或返回值执行 JSON 序列化的同步调用。 .NET 和 JavaScript 表示形式的内存管理和翻译的所有方面都留给开发人员。
警告
虽然使用 IJSUnmarshalledRuntime 作为 JS 互操作方法的开销最少,但与这些 API 交互所需的 JavaScript API 目前未记录,并且未来的版本可能会有重大更改。
function jsInteropCall() {
return BINDING.js_to_mono_obj("Hello world");
}
@inject IJSRuntime JS
@code {
protected override void OnInitialized()
{
var unmarshalledJs = (IJSUnmarshalledRuntime)JS;
var value = unmarshalledJs.InvokeUnmarshalled<string>("jsInteropCall");
}
}
使用 JavaScript [JSImport]
/[JSExport]
互操作
JavaScript [JSImport]
/[JSExport]
互操作对于 Blazor WebAssembly 应用程序,在 ASP.NET Core 于 .NET 7 发布之前的框架版本中提供了改进的性能和稳定性,优于之前的 JS 互操作 API。
有关详细信息,请参阅 JavaScript JSImport/JSExport 与 ASP.NET Core Blazor 互操作。