備註
這不是本文的最新版本。 關於目前版本,請參閱 本文的 .NET 10 版本。
警告
此版本的 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 的 Interop 呼叫。 相反地,下列方法會將 JS Interop 減少為單一呼叫:
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 Interop 呼叫合併為單一呼叫,通常只有在元件進行大量 JS Interop 呼叫時,才會顯著提升效能。
請考慮使用同步呼叫
從 .NET 呼叫 JavaScript
本節僅適用於用戶端元件。
不論所呼叫的程式碼是同步還是非同步的,JS Interop 通話都是非同步的。 為了確保元件能在伺服器端和客戶端呈現模式中相容,呼叫會以非同步方式進行。 在伺服器上,所有 JS Interop 呼叫都必須以非同步方式進行,因為這些呼叫會透過網路連線來傳送。
如果您確定元件只會在 WebAssembly 上執行,則可以選擇進行同步的 JS Interop 呼叫。 進行同步呼叫的額外負荷會略低於進行非同步呼叫,而且可減少轉譯週期,因為在等候結果時不會有中繼狀態。
若要在用戶端元件中進行從 .NET 到 JavaScript 的同步呼叫,請將 IJSRuntime 轉換成 IJSInProcessRuntime 以進行 JS Interop 呼叫:
@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 Blazor JavaScript 互通性 (JS Interop)。
從 JavaScript 呼叫 .NET
本節僅適用於用戶端元件。
不論所呼叫的程式碼是同步還是非同步的,JS Interop 通話都是非同步的。 為了確保元件能在伺服器端和客戶端呈現模式中相容,呼叫會以非同步方式進行。 在伺服器上,所有 JS Interop 呼叫都必須以非同步方式進行,因為這些呼叫會透過網路連線來傳送。
如果您確定元件只會在 WebAssembly 上執行,則可以選擇進行同步的 JS Interop 呼叫。 進行同步呼叫的額外負荷會略低於進行非同步呼叫,而且可減少轉譯週期,因為在等候結果時不會有中繼狀態。
若要在用戶端元件中從 JavaScript 同步呼叫 .NET,請使用 DotNet.invokeMethod 而非 DotNet.invokeMethodAsync。
同步呼叫正常運作的條件是:
本節僅適用於用戶端元件。
不論所呼叫的程式碼是同步還是非同步的,JS Interop 通話都是非同步的。 為了確保元件能在伺服器端和客戶端呈現模式中相容,呼叫會以非同步方式進行。 在伺服器上,所有 JS Interop 呼叫都必須以非同步方式進行,因為這些呼叫會透過網路連線來傳送。
如果您確定元件只會在 WebAssembly 上執行,則可以選擇進行同步的 JS Interop 呼叫。 進行同步呼叫的額外負荷會略低於進行非同步呼叫,而且可減少轉譯週期,因為在等候結果時不會有中繼狀態。
若要在用戶端元件中進行從 .NET 到 JavaScript 的同步呼叫,請將 IJSRuntime 轉換成 IJSInProcessRuntime 以進行 JS Interop 呼叫:
@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 Blazor JavaScript 互通性 (JS Interop)。
請考慮使用未分批的呼叫
本節僅適用於 Blazor WebAssembly 應用程式。
在 Blazor WebAssembly 上執行時,可以從 .NET 對 JavaScript 進行未封送的呼叫。 這些是同步呼叫,不會執行自變數或傳回值的 JSON 串行化。 .NET 與 JavaScript 表示法之間的記憶體管理和翻譯的所有層面都會留給開發人員。
警告
使用 IJSUnmarshalledRuntime 時,JS 的 Interop 方法帶來的額外負擔最小,且目前尚未記載與這些 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] Interop
在.NET 7中,適用於ASP.NET Core應用程式的JavaScript[JSImport]/[JSExport]互操作性Blazor WebAssembly,相比於ASP.NET Core之前框架版本的JS互操作API,提供了更佳的效能和穩定性。
如需更多資訊,請參閱 JavaScript JSImport/JSExport 與 ASP.NET Core 的互操作性 Blazor。