JavaScript [JSImport]/[JSExport] 与 ASP.NET Core Blazor 互操作

注意

此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

对于当前版本,请参阅此文的 .NET 8 版本

本文介绍如何使用 JavaScript (JS) [JSImport]/[JSExport] 互操作 API(针对采用 .NET 7 或更高版本的应用而发布)在客户端组件中与 JavaScript (JS) 交互。

Blazor 根据 IJSRuntime 接口提供自己的 JS 互操作机制。 在 Blazor 呈现模式中和在 Blazor Hybrid 应用中统一支持 Blazor 的 JS 互操作。 IJSRuntime 还使库作者能够生成可在 Blazor 生态系统中共享的 JS 互操作库,并且仍然是 Blazor 中建议的 JS 互操作方法。 请参阅以下文章:

本文介绍特定于 WebAssembly 上执行的客户端组件的替代 JS 互操作方法。 这些方法仅在预计在客户端 WebAssembly 上运行时才适用。 库作者可以使用这些方法优化 JS 互操作,方法是在执行代码期间检查应用是否在浏览器中的 WebAssembly 上运行 (OperatingSystem.IsBrowser)。 迁移到 .NET 7 或更高版本时,应使用本文中描述的方法替换已过时的拆收 JS 互操作 API。

注意

本文重点介绍客户端组件中的 JS 互操作。 有关在 JavaScript 应用中调用 .NET 的指南,请参阅从 JavaScript 运行 .NET

已过时的 JavaScript 互操作 API

使用 IJSUnmarshalledRuntime API 的拆收 JS 互操作在 .NET 7 或更高版本中的 ASP.NET Core 中已过时。 按照本文中的指导替换已过时的 API。

先决条件

下载并安装 .NET 7 或更高版本(如果尚且在系统上安装它,或者系统未安装最新版本)。

命名空间

本文中描述的 JS 互操作 API 由 System.Runtime.InteropServices.JavaScript 命名空间中的属性控制。

启用不安全的块

在应用的项目文件中启用 AllowUnsafeBlocks 属性,这允许 Roslyn 编译器中的代码生成器将指针用于 JS 互操作:

<PropertyGroup>
  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

警告

JS 互操作 API 需要启用 AllowUnsafeBlocks。 在 .NET 应用中实现自己的不安全代码时要小心,这可能会带来安全和稳定性风险。 有关详细信息,请参阅不安全的代码、指针类型和函数指针

从 .NET 调用 JavaScript

本节说明如何从 .NET 调用 JS 函数。

在下面的 CallJavaScript1 组件中:

  • CallJavaScript1 模块是从具有 JSHost.ImportAsync并置 JS 文件 异步导入的。
  • 导入的 getMessageJS 函数由 GetWelcomeMessage 调用。
  • 返回的欢迎消息字符串通过 message 字段显示在 UI 中。

CallJavaScript1.razor

@page "/call-javascript-1"
@rendermode InteractiveWebAssembly
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 1)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override async Task OnInitializedAsync()
    {
        await JSHost.ImportAsync("CallJavaScript1", 
            "../Components/Pages/CallJavaScript1.razor.js");

        message = GetWelcomeMessage();
    }
}
@page "/call-javascript-1"
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 1)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override async Task OnInitializedAsync()
    {
        await JSHost.ImportAsync("CallJavaScript1", 
            "../Pages/CallJavaScript1.razor.js");

        message = GetWelcomeMessage();
    }
}

注意

在代码中包含对 OperatingSystem.IsBrowser 的条件检查,以确保 JS 互操作仅由客户端上呈现的组件调用。 这对于以客户端组件为目标的库/NuGet 包至关重要,这些组件无法执行此 JS 互操作 API 提供的代码。

若要导入 JS 函数以从 C# 调用它,请使用与 JS 函数签名匹配的 C# 方法签名上的 [JSImport] 属性[JSImport] 属性的第一个参数是要导入的 JS 函数的名称,第二个参数是 JS 模块 的名称。

在以下示例中,getMessage 是一个 JS 函数,它为名为 CallJavaScript1 的模块返回 string。 C# 方法签名匹配:没有参数传递给 JS 函数,JS 函数返回 string。 JS 函数由 C# 代码中的 GetWelcomeMessage 调用。

CallJavaScript1.razor.cs

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Components.Pages;

[SupportedOSPlatform("browser")]
public partial class CallJavaScript1
{
    [JSImport("getMessage", "CallJavaScript1")]
    internal static partial string GetWelcomeMessage();
}

上述 CallJavaScript1 分部类的应用命名空间是 BlazorSample。 组件的命名空间为 BlazorSample.Components.Pages。 如果在本地测试应用中使用上述组件,请更新命名空间以匹配应用。 例如,如果应用的命名空间为 ContosoApp,则命名空间为 ContosoApp.Components.Pages。 有关详细信息,请参阅 ASP.NET Core Razor 组件

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Pages;

[SupportedOSPlatform("browser")]
public partial class CallJavaScript1
{
    [JSImport("getMessage", "CallJavaScript1")]
    internal static partial string GetWelcomeMessage();
}

上述 CallJavaScript1 分部类的应用命名空间是 BlazorSample。 组件的命名空间为 BlazorSample.Pages。 如果在本地测试应用中使用上述组件,请更新命名空间以匹配应用。 例如,如果应用的命名空间为 ContosoApp,则命名空间为 ContosoApp.Pages。 有关详细信息,请参阅 ASP.NET Core Razor 组件

在导入的方法签名中,可以将 .NET 类型用于参数和返回值,它们由运行时自动封送。 使用 JSMarshalAsAttribute<T> 控制导入的方法参数的封送方式。 例如,可以选择将 long 封送为 System.Runtime.InteropServices.JavaScript.JSType.NumberSystem.Runtime.InteropServices.JavaScript.JSType.BigInt。 可以将 Action/Func<TResult> 回调作为参数传递,这些回调被封送为可调用的 JS 函数。 可以同时传递 JS 和托管对象引用,它们被封送为代理对象,使对象在边界上保持活动状态,直到代理被垃圾回收。 还可以导入和导出具有 Task 结果的异步方法,这些结果被封送为 JS 承诺。 大多数封送类型在导入和导出方法上作为参数和返回值双向工作,本文后面的从 JavaScript 调用 .NET 部分对此进行了介绍。

下表指示受支持的类型映射。

.NET JavaScript Nullable TaskPromise JSMarshalAs 可选 Array of
Boolean Boolean 支持 支持 支持 不支持
Byte Number 支持 支持 支持 支持
Char String 支持 支持 支持 不支持
Int16 Number 支持 支持 支持 不支持
Int32 Number 支持 支持 支持 支持
Int64 Number 支持 支持 不支持 不支持
Int64 BigInt 支持 支持 不支持 不支持
Single Number 支持 支持 支持 不支持
Double Number 支持 支持 支持 支持
IntPtr Number 支持 支持 支持 不支持
DateTime Date 支持 支持 不支持 不支持
DateTimeOffset Date 支持 支持 不支持 不支持
Exception Error 不支持 支持 支持 不支持
JSObject Object 不支持 支持 支持 支持
String String 不支持 支持 支持 支持
Object Any 不支持 支持 不支持 支持
Span<Byte> MemoryView 不支持 不支持 不支持 不支持
Span<Int32> MemoryView 不支持 不支持 不支持 不支持
Span<Double> MemoryView 不支持 不支持 不支持 不支持
ArraySegment<Byte> MemoryView 不支持 不支持 不支持 不支持
ArraySegment<Int32> MemoryView 不支持 不支持 不支持 不支持
ArraySegment<Double> MemoryView 不支持 不支持 不支持 不支持
Task Promise 不支持 不支持 支持 不支持
Action Function 不支持 不支持 不支持 不支持
Action<T1> Function 不支持 不支持 不支持 不支持
Action<T1, T2> Function 不支持 不支持 不支持 不支持
Action<T1, T2, T3> Function 不支持 不支持 不支持 不支持
Func<TResult> Function 不支持 不支持 不支持 不支持
Func<T1, TResult> Function 不支持 不支持 不支持 不支持
Func<T1, T2, TResult> Function 不支持 不支持 不支持 不支持
Func<T1, T2, T3, TResult> Function 不支持 不支持 不支持 不支持

以下条件适用于类型映射和封送的值:

  • Array of 列指示是否可以将 .NET 类型封送为 JSArray。 示例:C# int[] (Int32) 映射为 Number 的 JSArray
  • 将 JS 值传递给具有错误类型的值的 C# 时,在大多数情况下,框架将引发异常。 框架不会在 JS 中执行编译时类型检查。
  • JSObjectExceptionTaskArraySegment 将创建 GCHandle 和代理。 你可以在开发人员代码中触发处置,或允许 .NET 垃圾回收 (GC) 来稍后处置对象。 这些类型会产生显著的性能开销。
  • Array:封送数组会在 JS 或 .NET 中创建数组的副本。
  • MemoryView
    • MemoryView 是 .NET WebAssembly 运行时用于封送 SpanArraySegment 的 JS 类。
    • 与封送数组不同,封送 SpanArraySegment 不会创建基础内存的副本。
    • MemoryView 只能由 .NET WebAssembly 运行时正确实例化。 因此,不能将 JS 函数导入为具有 SpanArraySegment 参数的 .NET 方法。
    • Span 创建的 MemoryView 仅在互操作调用期间有效。 由于在调用堆栈上分配了 Span,这在互操作调用之后不会保留,因此不能导出返回 Span 的 .NET 方法。
    • ArraySegment 创建的 MemoryView 会在互操作调用后存活,并且可用于共享缓冲区。 对为 ArraySegment 创建的 MemoryView 调用 dispose() 将处置代理并将取消固定基础 .NET 数组。 建议在 try-finally 块中为 MemoryView 调用 dispose()

下表指示受支持的类型映射。

.NET JavaScript Nullable TaskPromise JSMarshalAs 可选 Array of
Boolean Boolean 支持 支持 支持 不支持
Byte Number 支持 支持 支持 支持
Char String 支持 支持 支持 不支持
Int16 Number 支持 支持 支持 不支持
Int32 Number 支持 支持 支持 支持
Int64 Number 支持 支持 不支持 不支持
Int64 BigInt 支持 支持 不支持 不支持
Single Number 支持 支持 支持 不支持
Double Number 支持 支持 支持 支持
IntPtr Number 支持 支持 支持 不支持
DateTime Date 支持 支持 不支持 不支持
DateTimeOffset Date 支持 支持 不支持 不支持
Exception Error 不支持 支持 支持 不支持
JSObject Object 不支持 支持 支持 支持
String String 不支持 支持 支持 支持
Object Any 不支持 支持 不支持 支持
Span<Byte> MemoryView 不支持 不支持 不支持 不支持
Span<Int32> MemoryView 不支持 不支持 不支持 不支持
Span<Double> MemoryView 不支持 不支持 不支持 不支持
ArraySegment<Byte> MemoryView 不支持 不支持 不支持 不支持
ArraySegment<Int32> MemoryView 不支持 不支持 不支持 不支持
ArraySegment<Double> MemoryView 不支持 不支持 不支持 不支持
Task Promise 不支持 不支持 支持 不支持
Action Function 不支持 不支持 不支持 不支持
Action<T1> Function 不支持 不支持 不支持 不支持
Action<T1, T2> Function 不支持 不支持 不支持 不支持
Action<T1, T2, T3> Function 不支持 不支持 不支持 不支持
Func<TResult> Function 不支持 不支持 不支持 不支持
Func<T1, TResult> Function 不支持 不支持 不支持 不支持
Func<T1, T2, TResult> Function 不支持 不支持 不支持 不支持
Func<T1, T2, T3, TResult> Function 不支持 不支持 不支持 不支持

以下条件适用于类型映射和封送的值:

  • Array of 列指示是否可以将 .NET 类型封送为 JSArray。 示例:C# int[] (Int32) 映射为 Number 的 JSArray
  • 将 JS 值传递给具有错误类型的值的 C# 时,在大多数情况下,框架将引发异常。 框架不会在 JS 中执行编译时类型检查。
  • JSObjectExceptionTaskArraySegment 将创建 GCHandle 和代理。 你可以在开发人员代码中触发处置,或允许 .NET 垃圾回收 (GC) 来稍后处置对象。 这些类型会产生显著的性能开销。
  • Array:封送数组会在 JS 或 .NET 中创建数组的副本。
  • MemoryView
    • MemoryView 是 .NET WebAssembly 运行时用于封送 SpanArraySegment 的 JS 类。
    • 与封送数组不同,封送 SpanArraySegment 不会创建基础内存的副本。
    • MemoryView 只能由 .NET WebAssembly 运行时正确实例化。 因此,不能将 JS 函数导入为具有 SpanArraySegment 参数的 .NET 方法。
    • Span 创建的 MemoryView 仅在互操作调用期间有效。 由于在调用堆栈上分配了 Span,这在互操作调用之后不会保留,因此不能导出返回 Span 的 .NET 方法。
    • ArraySegment 创建的 MemoryView 会在互操作调用后存活,并且可用于共享缓冲区。 对为 ArraySegment 创建的 MemoryView 调用 dispose() 将处置代理并将取消固定基础 .NET 数组。 建议在 try-finally 块中为 MemoryView 调用 dispose()

[JSImport] 属性中的模块名称和在具有 JSHost.ImportAsync 的组件中加载模块的调用必须匹配并且在应用中必须唯一。 为 NuGet 包中的部署创作库时,建议使用 NuGet 包命名空间作为模块名称的前缀。 在以下示例中,模块名称反映了 Contoso.InteropServices.JavaScript 包和用户消息互操作类 (UserMessages) 的文件夹:

[JSImport("getMessage", 
    "Contoso.InteropServices.JavaScript.UserMessages.CallJavaScript1")]

可以通过在函数名称中使用 globalThis 前缀并使用 [JSImport] 属性来导入全局命名空间上可访问的函数,而不提供模块名称。 在下面的示例中,console.logglobalThis 为前缀。 导入的函数由 C# Log 方法调用,该方法接受 C# 字符串消息 (message) 并将 C# 字符串封送到 console.log 的 JSString

[JSImport("globalThis.console.log")]
internal static partial void Log([JSMarshalAs<JSType.String>] string message);

从标准 JavaScript ES6 模块中导出脚本,该脚本可以与组件并置,也可以与其他 JavaScript 静态资源一起放置在 JS 文件中(例如 wwwroot/js/{FILE NAME}.js,其中 JS 静态资产保存在应用的 wwwroot 文件夹中名为 js 的文件夹中,{FILE NAME} 占位符是文件名)。

在以下示例中,名为 getMessage 的 JS 函数从并置的 JS 文件中导出,该文件返回使用葡萄牙语的欢迎消息“Hello from Blazor!”:

CallJavaScript1.razor.js

export function getMessage() {
  return 'Olá do Blazor!';
}

从 JavaScript 调用 .NET

本节说明如何从 JS 调用 .NET 方法。

以下 CallDotNet1 组件调用 JS 直接与 DOM 交互以呈现欢迎消息字符串:

  • CallDotNetJS 模块 是从该组件的并置 JS 文件异步导入的。
  • 导入的 setMessageJS 函数由 SetWelcomeMessage 调用。
  • 返回的欢迎消息由 setMessage 通过 message 字段显示在 UI 中。

重要

在本节的示例中,JS 互操作用于在 OnAfterRender 中呈现组件后改变 DOM 元素(纯粹出于演示目的)。 通常,当对象不与 Blazor 交互时,应只使用 JS 改变 DOM。 本节中显示的方法类似于在 Razor 组件中使用第三方 JS 库的情况,其中组件通过 JS 互操作与 JS 库交互,第三方 JS 库与 DOM 的一部分进行交互,并且 Blazor 不直接参与对 DOM 的该部分的 DOM 更新。 有关详细信息,请参阅 ASP.NET Core BlazorJavaScript 互操作性(JS 互操作)

CallDotNet1.razor

@page "/call-dotnet-1"
@rendermode InteractiveWebAssembly
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call .NET Example 1)
</h1>

<p>
    <span id="result">.NET method not executed yet</span>
</p>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSHost.ImportAsync("CallDotNet1", 
                "../Components/Pages/CallDotNet1.razor.js");

            SetWelcomeMessage();
        }
    }
}
@page "/call-dotnet-1"
@using System.Runtime.InteropServices.JavaScript

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call .NET Example 1)
</h1>

<p>
    <span id="result">.NET method not executed yet</span>
</p>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSHost.ImportAsync("CallDotNet1", 
                "../Pages/CallDotNet1.razor.js");

            SetWelcomeMessage();
        }
    }
}

若要导出 .NET 方法以便可以从 JS 进行调用,请使用 [JSExport] 属性

如下示例中:

  • SetWelcomeMessage 调用名为 setMessage 的 JS 函数。 JS 函数调用 .NET 以接收来自 GetMessageFromDotnet 的欢迎消息并在 UI 中显示该消息。
  • GetMessageFromDotnet 是具有 [JSExport] 属性的 .NET 方法,它返回使用葡萄牙语的欢迎消息“Hello from Blazor!”。

CallDotNet1.razor.cs

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Components.Pages;

[SupportedOSPlatform("browser")]
public partial class CallDotNet1
{
    [JSImport("setMessage", "CallDotNet1")]
    internal static partial void SetWelcomeMessage();

    [JSExport]
    internal static string GetMessageFromDotnet()
    {
        return "Olá do Blazor!";
    }
}

上述 CallDotNet1 分部类的应用命名空间是 BlazorSample。 组件的命名空间为 BlazorSample.Components.Pages。 如果在本地测试应用中使用上述组件,请更新应用的命名空间以匹配应用。 例如,如果应用的命名空间是 ContosoApp,则组件命名空间是 ContosoApp.Components.Pages。 有关详细信息,请参阅 ASP.NET Core Razor 组件

在以下示例中,名为 setMessage 的 JS 函数是从并置的 JS 文件中导入的。

setMessage 方法:

  • 调用 globalThis.getDotnetRuntime(0) 以公开用于调用导出的 .NET 方法的 WebAssembly .NET 运行时实例。
  • 获取应用程序集的 JS 导出。 以下示例中应用程序集的名称为 BlazorSample
  • 从导出 (exports) 调用 BlazorSample.Components.Pages.CallDotNet1.GetMessageFromDotnet 方法。 返回值(即欢迎消息)分配给 CallDotNet1 组件的 <span> 文本。 应用的命名空间是 BlazorSampleCallDotNet1 组件的命名空间是 BlazorSample.Components.Pages

CallDotNet1.razor.js

export async function setMessage() {
  const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
  var exports = await getAssemblyExports("BlazorSample.dll");

  document.getElementById("result").innerText = 
    exports.BlazorSample.Components.Pages.CallDotNet1.GetMessageFromDotnet();
}
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.Pages;

[SupportedOSPlatform("browser")]
public partial class CallDotNet1
{
    [JSImport("setMessage", "CallDotNet1")]
    internal static partial void SetWelcomeMessage();

    [JSExport]
    internal static string GetMessageFromDotnet()
    {
        return "Olá do Blazor!";
    }
}

上述 CallDotNet1 分部类的应用命名空间是 BlazorSample。 组件的命名空间为 BlazorSample.Pages。 如果在本地测试应用中使用上述组件,请更新应用的命名空间以匹配应用。 例如,如果应用的命名空间是 ContosoApp,则组件命名空间是 ContosoApp.Pages。 有关详细信息,请参阅 ASP.NET Core Razor 组件

在以下示例中,名为 setMessage 的 JS 函数是从并置的 JS 文件中导入的。

setMessage 方法:

  • 调用 globalThis.getDotnetRuntime(0) 以公开用于调用导出的 .NET 方法的 WebAssembly .NET 运行时实例。
  • 获取应用程序集的 JS 导出。 以下示例中应用程序集的名称为 BlazorSample
  • 从导出 (exports) 调用 BlazorSample.Pages.CallDotNet1.GetMessageFromDotnet 方法。 返回值(即欢迎消息)分配给 CallDotNet1 组件的 <span> 文本。 应用的命名空间是 BlazorSampleCallDotNet1 组件的命名空间是 BlazorSample.Pages

CallDotNet1.razor.js

export async function setMessage() {
  const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
  var exports = await getAssemblyExports("BlazorSample.dll");

  document.getElementById("result").innerText = 
    exports.BlazorSample.Pages.CallDotNet1.GetMessageFromDotnet();
}

注意

调用 getAssemblyExports 以获取导出可以在 JavaScript 初始化程序 中进行,以便在整个应用中可用。

多个模块导入调用

加载 JS 模块后,只要应用在浏览器窗口或选项卡中运行,并且无需用户手动重新加载应用,该模块的 JS 函数就可供应用的组件和类使用。 在以下情况下,可以在同一模块上多次调用 JSHost.ImportAsync 而不会显着降低性能:

跨组件使用单个 JavaScript 模块

在遵循本节中的指导之前,请阅读本文的从 .NET 调用 JavaScript从 JavaScript 调用 .NET 部分,它们提供了有关 [JSImport]/[JSExport] 互操作的指导。

本部分中的示例说明如何在客户端应用中使用来自共享 JS 模块的 JS 互操作。 本部分中的指南不适用于 Razor 类库 (RCL)。

使用了以下组件、类、C# 方法和 JS 函数:

  • Interop 类 (Interop.cs):为名为 Interop 的模块设置导入和导出与 [JSImport][JSExport] 属性的 JS 互操作。
    • GetWelcomeMessage:调用导入的 getMessageJS 函数的 .NET 方法。
    • SetWelcomeMessage:调用导入的 setMessageJS 函数的 .NET 方法。
    • GetMessageFromDotnet:从 JS 调用时返回欢迎消息字符串的导出 C# 方法。
  • wwwroot/js/interop.js 文件:包含 JS 函数。
    • getMessage:在组件中被 C# 代码调用时返回欢迎消息。
    • setMessage:调用 GetMessageFromDotnet C# 方法并将返回的欢迎消息分配给 DOM <span> 元素。
  • Program.cs 调用 JSHost.ImportAsync 以从 wwwroot/js/interop.js 加载模块。
  • CallJavaScript2 组件 (CallJavaScript2.razor):调用 GetWelcomeMessage 并在组件的 UI 中显示返回的欢迎消息。
  • CallDotNet2 组件 (CallDotNet2.razor):调用 SetWelcomeMessage

Interop.cs

using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Versioning;

namespace BlazorSample.JavaScriptInterop;

[SupportedOSPlatform("browser")]
public partial class Interop
{
    [JSImport("getMessage", "Interop")]
    internal static partial string GetWelcomeMessage();

    [JSImport("setMessage", "Interop")]
    internal static partial void SetWelcomeMessage();

    [JSExport]
    internal static string GetMessageFromDotnet()
    {
        return "Olá do Blazor!";
    }
}

在上述示例中,应用的命名空间是 BlazorSample,而 C# 互操作类的完整命名空间是 BlazorSample.JavaScriptInterop

wwwroot/js/interop.js

export function getMessage() {
  return 'Olá do Blazor!';
}

export async function setMessage() {
  const { getAssemblyExports } = await globalThis.getDotnetRuntime(0);
  var exports = await getAssemblyExports("BlazorSample.dll");

  document.getElementById("result").innerText =
    exports.BlazorSample.JavaScriptInterop.Interop.GetMessageFromDotnet();
}

使 Program.cs 文件顶部的 System.Runtime.InteropServices.JavaScript 命名空间可用:

using System.Runtime.InteropServices.JavaScript;

在调用 WebAssemblyHost.RunAsync 之前在 Program.cs 中加载模块:

if (OperatingSystem.IsBrowser())
{
    await JSHost.ImportAsync("Interop", "../js/interop.js");
}

CallJavaScript2.razor

@page "/call-javascript-2"
@rendermode InteractiveWebAssembly
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 2)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override void OnInitialized()
    {
        message = Interop.GetWelcomeMessage();
    }
}
@page "/call-javascript-2"
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop 
    (Call JS Example 2)
</h1>

@(message is not null ? message : string.Empty)

@code {
    private string? message;

    protected override void OnInitialized()
    {
        message = Interop.GetWelcomeMessage();
    }
}

CallDotNet2.razor

@page "/call-dotnet-2"
@rendermode InteractiveWebAssembly
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop  
    (Call .NET Example 2)
</h1>

<p>
    <span id="result">.NET method not executed</span>
</p>

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            Interop.SetWelcomeMessage();
        }
    }
}
@page "/call-dotnet-2"
@using BlazorSample.JavaScriptInterop

<h1>
    JS <code>[JSImport]</code>/<code>[JSExport]</code> Interop  
    (Call .NET Example 2)
</h1>

<p>
    <span id="result">.NET method not executed</span>
</p>

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            Interop.SetWelcomeMessage();
        }
    }
}

重要

在本节的示例中,JS 互操作用于在 OnAfterRender 中呈现组件后改变 DOM 元素(纯粹出于演示目的)。 通常,当对象不与 Blazor 交互时,应只使用 JS 改变 DOM。 本节中显示的方法类似于在 Razor 组件中使用第三方 JS 库的情况,其中组件通过 JS 互操作与 JS 库交互,第三方 JS 库与 DOM 的一部分进行交互,并且 Blazor 不直接参与对 DOM 的该部分的 DOM 更新。 有关详细信息,请参阅 ASP.NET Core BlazorJavaScript 互操作性(JS 互操作)

其他资源