JavaScript [JSImport]/[JSExport] Interop 搭配 ASP.NET Core Blazor

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

本文說明如何使用針對採用 .NET 7 或更新版本的應用程式的 JavaScript (JS) [JSImport]/[JSExport] Interop API,與用戶端元件中的 JavaScript (JS) 互動。

Blazor 會根據 IJSRuntime 介面提供自己的 JS Interop 機制。 Blazor 跨轉譯模式和 Blazor Hybrid 應用程式都一致支援 Blazor 的 JS Interop。 IJSRuntime 也可讓程式庫作者建置可跨 Blazor 生態系統共用的 JS Interop 程式庫,並且仍然是 Blazor 中 JS Interop 的建議方法。 請參閱以下文章:

本文描述在 WebAssembly 上執行的用戶端元件特定的替代 JS Interop 方法。 當您只預期在用戶端 WebAssembly 上執行時,這些方法就很合適。 程式庫作者可以使用這些方法來最佳化 JS Interop,方法是在程式碼執行期間檢查應用程式是否在瀏覽器 (OperatingSystem.IsBrowser) 中的 WebAssembly 上執行。 本文所述的方法應該用來在移轉至 .NET 7 或更新版本時取代過時的解除封送 JS Interop API。

注意

本文著重於用戶端元件中的 JS Interop。 如需在 JavaScript 應用程式中呼叫 .NET 的指導,請參閱從 JavaScript 執行 .NET

過時的 JavaScript Interop API

使用 IJSUnmarshalledRuntime API 解除封送的 JS Interop 在 .NET 7 或更新版本的 ASP.NET Core 中已淘汰。 遵循本文中的指導來取代過時的 API。

必要條件

如果尚未安裝在系統上,或系統尚未安裝最新版本,請下載並安裝 .NET 7 或更新版本

Namespace

本文所述的 JS Interop API 是由 System.Runtime.InteropServices.JavaScript 命名空間中的屬性所控制。

啟用不安全的區塊

啟用應用程式專案檔中的 AllowUnsafeBlocks 屬性,其會允許 Roslyn 編譯器中的程式碼產生器使用指標進行 JS Interop:

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

警告

JS Interop API 需要啟用 AllowUnsafeBlocks。 在 .NET 應用程式中實作本身不安全的程式碼時請小心,這可能會帶來安全性和穩定性風險。 如需詳細資訊,請參閱不安全的程式碼、指標型別和函式指標

從 .NET 呼叫 JavaScript

本節說明如何從 .NET 呼叫 JS 函式。

在下列 CallJavaScript1 元件中:

  • CallJavaScript1 模組會以非同步方式從共置的 JS 檔案使用 JSHost.ImportAsync 匯入。
  • 匯入的 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 Interop 只會由用戶端上轉譯的元件呼叫。 對於以伺服器端元件為目標的程式庫/NuGet 封裝 (其無法執行此 JS Interop API 所提供的程式碼),這很重要。

若要匯入 JS 函式以從 C# 呼叫它,請在符合 JS 函式簽章的 C# 方法簽章上使用 [JSImport] 屬性[JSImport] 屬性的第一個參數是要匯入 JS 函式的名稱,而第二個參數是 JS 模組的名稱。

在下列範例中,getMessage 是針對名為 CallJavaScript1 的模組傳回 string 的 JS 函式。 C# 方法簽章相符:不會將任何參數傳遞至 JS 函式,而 JS 函式會傳回 string。 JS 函式是由 GetWelcomeMessage 在 C# 程式碼中呼叫。

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 和受控物件參考,而且它們會封送處理為 Proxy 物件,讓物件在跨邊界保持運作,直到 Proxy 被垃圾回收為止。 您也可以使用 Task 結果匯入和匯出非同步方法,這些方法會按照 JS Promises 進行封送處理。 大部分封送處理型別可以以參數和傳回值形式,在匯入和匯出方法上雙向運作,本文稍後的從 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) 對應至 JSNumberArray
  • 將 JS 值傳遞至使用錯誤型別值的 C# 時,架構在大部分情況下會擲回例外狀況。 架構不會在 JS 中執行編譯時間型別檢查。
  • JSObjectExceptionTaskArraySegment 建立 GCHandle 及 Proxy。 您可以在開發人員程式碼中觸發處置,或稍後允許 .NET 記憶體回收 (GC) 處置物件。 這些型別具有顯著的效能額外負荷。
  • Array:封送處理陣列會在 JS 或 .NET 中建立陣列的複本。
  • MemoryView
    • MemoryView 是 .NET WebAssembly 執行階段的 JS 類別,用於封送處理 SpanArraySegment
    • 與封送處理陣列不同,封送處理 SpanArraySegment 不會建立基礎記憶體的複本。
    • MemoryView 只能由 .NET WebAssembly 執行階段正確具現化。 因此,無法將 JS 函式匯入為具有 SpanArraySegment 參數的 .NET 方法。
    • Span 建立的 MemoryView 只在 Interop 呼叫期間才有效。 如同 Span 在呼叫堆疊上配置,在 Interop 呼叫之後不會保存,因此無法匯出傳回 Span 的 .NET 方法。
    • 針對 ArraySegment 建立的 MemoryView 在 Interop 呼叫之後存留下來,而且有助於共用緩衝區。 在針對 ArraySegment 建立的 MemoryView 上呼叫 dispose() 會處置 Proxy,並取消釘選基礎 .NET 陣列。 我們建議在 MemoryViewdispose() 區塊中呼叫 try-finally

下表指出支援的型別對應。

.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) 對應至 JSNumberArray
  • 將 JS 值傳遞至使用錯誤型別值的 C# 時,架構在大部分情況下會擲回例外狀況。 架構不會在 JS 中執行編譯時間型別檢查。
  • JSObjectExceptionTaskArraySegment 建立 GCHandle 及 Proxy。 您可以在開發人員程式碼中觸發處置,或稍後允許 .NET 記憶體回收 (GC) 處置物件。 這些型別具有顯著的效能額外負荷。
  • Array:封送處理陣列會在 JS 或 .NET 中建立陣列的複本。
  • MemoryView
    • MemoryView 是 .NET WebAssembly 執行階段的 JS 類別,用於封送處理 SpanArraySegment
    • 與封送處理陣列不同,封送處理 SpanArraySegment 不會建立基礎記憶體的複本。
    • MemoryView 只能由 .NET WebAssembly 執行階段正確具現化。 因此,無法將 JS 函式匯入為具有 SpanArraySegment 參數的 .NET 方法。
    • Span 建立的 MemoryView 只在 Interop 呼叫期間才有效。 如同 Span 在呼叫堆疊上配置,在 Interop 呼叫之後不會保存,因此無法匯出傳回 Span 的 .NET 方法。
    • 針對 ArraySegment 建立的 MemoryView 在 Interop 呼叫之後存留下來,而且有助於共用緩衝區。 在針對 ArraySegment 建立的 MemoryView 上呼叫 dispose() 會處置 Proxy,並取消釘選基礎 .NET 陣列。 我們建議在 MemoryViewdispose() 區塊中呼叫 try-finally

[JSImport] 屬性中的模組名稱,以及使用 JSHost.ImportAsync 在元件中載入模組的呼叫必須相符且在應用程式中是唯一的。 撰寫程式庫以在 NuGet 封裝中部署時,建議您使用 NuGet 封裝命名空間作為模組名稱的前置詞。 在下列範例中,模組名稱會反映 Contoso.InteropServices.JavaScript 封裝和使用者訊息 Interop 類別的資料夾 (UserMessages):

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

可在全域命名空間上存取的函式,可以在函式名稱中使用 globalThis 前置詞,以及使用 [JSImport] 屬性而不提供模組名稱來匯入。 在下列範例中,console.log 的前置詞為 globalThis。 匯入的函式是由 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 元件會呼叫直接與 DOM 互動以轉譯歡迎訊息字串的 JS:

  • CallDotNetJS 模組會從此元件的共置 JS 檔案非同步匯入。
  • 匯入的 setMessageJS 函式是由 SetWelcomeMessage 呼叫。
  • 傳回的歡迎訊息字串會由 setMessage 透過 message 欄位顯示在 UI 中。

重要

在本節的範例中,JS Interop 是用來在 OnAfterRender 中轉譯元件之後,純粹為了示範目的而變動 DOM 元素。 一般而言,您只應該在物件未與 Blazor 互動時使用 JS 來變動 DOM。 本節中顯示的方法與 Razor 元件中使用的第三方 JS 程式庫的情況類似,其中元件會透過 JS Interop 與 JS 程式庫互動,第三方 JS 程式庫會與 DOM 的一部分互動,而 Blazor 不會直接與對 DOM 部分的 DOM 更新相關。 如需詳細資訊,請參閱 ASP.NET Core Blazor JavaScript 互通性 (JS Interop)

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 是 .NET 方法,其 [JSExport] 屬性會以葡萄牙文傳回歡迎訊息 "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) 以公開 WebAssembly .NET 執行階段執行個體,以呼叫匯出的 .NET 方法。
  • 取得應用程式元件的 JS 匯出。 下列範例中應用程式元件的名稱為 BlazorSample
  • 從匯出 (exports) 呼叫 BlazorSample.Components.Pages.CallDotNet1.GetMessageFromDotnet 方法。 傳回的值,即歡迎訊息,會指派給 CallDotNet1 元件的 <span> 文字。 應用程式的命名空間為 BlazorSample,而 CallDotNet1 元件的命名空間為 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) 以公開 WebAssembly .NET 執行階段執行個體,以呼叫匯出的 .NET 方法。
  • 取得應用程式元件的 JS 匯出。 下列範例中應用程式元件的名稱為 BlazorSample
  • 從匯出 (exports) 呼叫 BlazorSample.Pages.CallDotNet1.GetMessageFromDotnet 方法。 傳回的值,即歡迎訊息,會指派給 CallDotNet1 元件的 <span> 文字。 應用程式的命名空間為 BlazorSample,而 CallDotNet1 元件的命名空間為 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 可以在相同的模組上呼叫多次,而不會在下列情況下大幅降低效能:

  • 使用者會瀏覽呼叫 JSHost.ImportAsync 以匯入模組的元件、導覽離開元件,然後返回元件,在其中會再次呼叫 JSHost.ImportAsync 以取得相同模組匯入。
  • 相同模組會由不同的元件使用,並由 JSHost.ImportAsync 在每個元件中載入。

跨元件使用單一 JavaScript 模組

遵循本節中的指導之前,請閱讀本文的從 .NET 呼叫 JavaScript從 JavaScript 呼叫 .NET 小節,其中提供有關 [JSImport]/[JSExport] Interop 的一般指導。

本節中的範例示範如何從用戶端應用程式中的共用 JS 模組使用 JS Interop。 本節中的指導不適用 Razor 類別庫 (RCL)。

使用下列元件、類別、C# 方法和 JS 函式:

  • Interop 類別 (Interop.cs):使用名為 Interop 的模組的 [JSImport][JSExport] 屬性,設定匯入和匯出 JS Interop。
    • GetWelcomeMessage:呼叫匯入的 getMessageJS 函式的 .NET 方法。
    • SetWelcomeMessage:呼叫匯入的 setMessageJS 函式的 .NET 方法。
    • GetMessageFromDotnet:匯出的 C# 方法,會在從 JS 呼叫時傳回歡迎訊息字串。
  • wwwroot/js/interop.js 檔案:包含 JS 函式。
    • getMessage:在元件中由 C# 程式碼呼叫時傳回歡迎訊息。
    • setMessage:呼叫 GetMessageFromDotnet C# 方法,並將傳回的歡迎訊息指派給 DOM <span> 元素。
  • Program.cs 呼叫 JSHost.ImportAsync 以從 wwwroot/js/interop.js 載入模組。
  • CallJavaScript2 元件 (CallJavaScript2.razor):在元件的 UI 中呼叫 GetWelcomeMessage 並顯示傳回的歡迎訊息。
  • 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# Interop 類別的完整命名空間為 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();
}

System.Runtime.InteropServices.JavaScript 命名空間在 Program.cs 檔案頂端提供:

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 Interop 是用來在 OnAfterRender 中轉譯元件之後,純粹為了示範目的而變動 DOM 元素。 一般而言,您只應該在物件未與 Blazor 互動時使用 JS 來變動 DOM。 本節中顯示的方法與 Razor 元件中使用的第三方 JS 程式庫的情況類似,其中元件會透過 JS Interop 與 JS 程式庫互動,第三方 JS 程式庫會與 DOM 的一部分互動,而 Blazor 不會直接與對 DOM 部分的 DOM 更新相關。 如需詳細資訊,請參閱 ASP.NET Core Blazor JavaScript 互通性 (JS Interop)

其他資源