共用方式為


ASP.NET Core Blazor JavaScript 互通性 (JS interop)

注意

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

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本

重要

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

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

Blazor 應用程式可以從 JS 函式的 .NET 方法和 .NET 方法叫用 JavaScript (JS) 函式。 這些案例稱為 JavaScript 互通性 (JS interop)。

下列文章提供進一步的 JS Interop 指引:

注意

JavaScript [JSImport]/[JSExport] 互通性 API 可用於 .NET 7 或更新版本中的 ASP.NET Core 中的用戶端元件。

如需詳細資訊,請參閱 JavaScript JSImport/JSExport Interop 搭配 ASP.NET Core Blazor

壓縮具有不受信任資料的互動式伺服器元件

透過預設啟用的壓縮,避免建立安全 (經過驗證/授權) 的互動式伺服器端元件來呈現來自不受信任來源的資料。 不受信任的來源包括路由參數、查詢字串、來自 JS 互通性的資料,以及第三方使用者可以控制的任何其他資料來源 (資料庫、外部服務)。 如需詳細資訊,請參閱 ASP.NET Core BlazorSignalR 指引ASP.NET Core Blazor 互動式伺服器端轉譯的威脅防護指引

JavaScript Interop 抽象概念和功能封裝

@microsoft/dotnet-js-interop 套件 (npmjs.com) (Microsoft.JSInterop NuGet 套件) 可提供 .NET 與 JavaScript (JS) 程式碼之間互通性的抽象概念和功能。 dotnet/aspnetcore GitHub 存放庫 (/src/JSInterop 資料夾) 中提供參考原始碼。 如需詳細資訊,請參閱 GitHub 存放庫的 README.md 檔案。

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

在 TypeScript 中撰寫 JS Interop 指令碼的其他資源:

與 DOM 互動

只有當物件未與 Blazor 互動時,才使用 JavaScript (JS) 變動 DOM。 Blazor 會維護 DOM 的表示法,並與 DOM 物件直接互動。 如果使用 JS 直接或透過 JS Interop 在外部修改 Blazor 所轉譯的專案,DOM 可能不再符合 Blazor 的內部表示法,這可能會導致未定義的行為。 未定義的行為可能僅僅會干擾元素或其函式的轉譯,但也可能會對應用程式或伺服器造成安全性風險。

本指南不僅適用於您自己的 JS Interop 程式碼,也適用於應用程式所使用的任何 JS 程式庫,包括協力廠商架構所提供的任何項目,例如 Bootstrap JSjQuery

在一些文件範例中,JS Interop 是單純就示範目的用來將元素進行變動,做為範例的一部分。 在這些案例中,會在文字中出現警告。

如需詳細資訊,請參閱從 ASP.NET Core Blazor 中的 .NET 方法呼叫 JavaScript 函式

具有類型函式欄位的 JavaScript 類別

BlazorJS Interop 不支援具有函式類型欄位的 JavaScript 類別。 在類別中使用 Javascript 函式。

不支援:GreetingHelpers.sayHello 在以下類別中作為函式類型欄位的 Blazor's JS 互操作沒有發現,因此無法從 C# 程式碼執行:

export class GreetingHelpers {
  sayHello = function() {
    ...
  }
}

支援:GreetingHelpers.sayHello 在下列類別中作為函式是受支援的:

export class GreetingHelpers {
  sayHello() {
    ...
  }
}

也支援箭頭函式:

export class GreetingHelpers {
  sayHello = () => {
    ...
  }
}

避免內嵌事件處理程式

JavaScript 函式可以直接從內嵌事件處理器中呼叫。 在下面的範例中,alertUser是在使用者選取按鈕時呼叫的 JavaScript 函式:

<button onclick="alertUser">Click Me!</button>

然而,在呼叫 JavaScript 函式時,使用內嵌事件處理程式是一個差勁的設計選擇

我們建議避免使用內嵌事件處理程式,而改用在 JavaScript 中以 addEventListener 指派處理程式的方法,就像以下範例所展示的一樣:

AlertUser.razor.js

export function alertUser() {
  alert('The button was selected!');
}

export function addHandlers() {
  const btn = document.getElementById("btn");
  btn.addEventListener("click", alertUser);
}

AlertUser.razor

@page "/alert-user"
@implements IAsyncDisposable
@inject IJSRuntime JS

<h1>Alert User</h1>

<p>
    <button id="btn">Click Me!</button>
</p>

@code {
    private IJSObjectReference? module;

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>("import",
                "./Components/Pages/AlertUser.razor.js");

            await module.InvokeVoidAsync("addHandlers");
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

如需詳細資訊,請參閱以下資源:

非同步 JavaScript 呼叫

不論所呼叫的是同步或非同步程式碼,JS Interop 呼叫都是非同步的。 呼叫會以非同步方式進行,以確保元件在伺服器端與用戶端轉譯模式可以相容。 採用伺服器端轉譯時,JS Interop 呼叫必須以非同步方式進行,因為這些呼叫會透過網路連線來傳送。 對於獨佔採用用戶端轉譯的應用程式,支援同步 JS Interop 呼叫。

物件序列化

Blazor 會使用 System.Text.Json 進行下列需求和預設行為的序列化:

  • 類型必須具有預設建構函式、get/set 存取子必須是公用的,而且永遠不會將欄位序列化。
  • 無法自訂全域預設序列化,以避免中斷現有的元件庫、對效能和安全性產生影響,以及降低可靠性。
  • 序列化 .NET 成員名稱會產生小寫 JSON 機碼名稱。
  • JSON 會還原序列化為 JsonElement C# 執行個體,以允許混合大小寫。 儘管在 JSON 索引鍵名稱和 C# 屬性名稱之間有任何大小寫差異,指派給 C# 模型屬性的內部轉換會如預期般運作。
  • 複雜的架構類型 (例如 KeyValuePair) 可能會在發佈時被 IL 修剪器修剪掉,且不存在於 JS 互通性中。 建議您為 IL 修剪器所修剪掉的類型建立自訂類型。
  • Blazor 一律依賴 JSON 序列化的反映,包括使用 C# 來源產生時。 將 JsonSerializerIsReflectionEnabledByDefault 設定為應用程式專案檔中的 false 會導致嘗試序列化時發生錯誤。

JsonConverter API 可用於自訂序列化。 您可以使用 [JsonConverter] 屬性來標註屬性,以覆寫現有資料類型的預設序列化。

如需詳細資訊,請參閱 .NET 文件中的下列資源:

Blazor 支援最佳化的位元組陣列 JS Interop,可避免將位元組陣列編碼/解碼為 Base64。 應用程式可以套用自訂序列化,並傳遞所產生的位元組。 如需詳細資訊,請參閱從 ASP.NET Core Blazor 中的 .NET 方法呼叫 JavaScript 函式

當大量 .NET 物件快速序列化,或當大型 .NET 物件或許多 .NET 物件必須進行序列化時,Blazor 支援解除封送的 JS Interop。 如需詳細資訊,請參閱從 ASP.NET Core Blazor 中的 .NET 方法呼叫 JavaScript 函式

元件處置期間的 DOM 清除工作

請勿在元件處置期間執行 DOM 清除工作的 JS Interop 程式碼。 基於下列原因,請在用戶端上改用 JavaScript (JS) 的 MutationObserver 模式:

  • 當您的清除程式碼在 Dispose{Async} 中執行時,元件可能已經從 DOM 中移除。
  • 在伺服器端轉譯期間,當您的清除程式碼在 Blazor 中執行時,架構可能已經處置 Dispose{Async} 轉譯器。

MutationObserver 模式可讓您在從 DOM 移除元素時執行函式。

在下列範例中,DOMCleanup 元件:

  • 包含一個 idcleanupDiv<div>。 從 DOM 移除元件時,會從 DOM 移除 <div> 元素,以及元件的 rest DOM 標記。
  • DOMCleanup.razor.js 檔案載入 DOMCleanupJS 類別,並呼叫其 createObserver 函式來設定 MutationObserver 回呼。 這些工作是在 OnAfterRenderAsync 生命週期方法中完成。

DOMCleanup.razor

@page "/dom-cleanup"
@implements IAsyncDisposable
@inject IJSRuntime JS

<h1>DOM Cleanup Example</h1>

<div id="cleanupDiv"></div>

@code {
    private IJSObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>(
                "import", "./Components/Pages/DOMCleanup.razor.js");

            await module.InvokeVoidAsync("DOMCleanup.createObserver");
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

在下列範例中,每次發生 DOM 變更時,都會執行 MutationObserver 回呼。 當 if 陳述式確認已移除目標元素 (cleanupDiv) 時,執行您的清除程式碼 (if (targetRemoved) { ... })。 請務必中斷連線並刪除 MutationObserver,以避免清除程式碼執行之後記憶體流失。

DOMCleanup.razor.js 與上述 DOMCleanup 元件並排放置:

export class DOMCleanup {
  static observer;

  static createObserver() {
    const target = document.querySelector('#cleanupDiv');

    this.observer = new MutationObserver(function (mutations) {
      const targetRemoved = mutations.some(function (mutation) {
        const nodes = Array.from(mutation.removedNodes);
        return nodes.indexOf(target) !== -1;
      });

      if (targetRemoved) {
        // Cleanup resources here
        // ...

        // Disconnect and delete MutationObserver
        this.observer && this.observer.disconnect();
        delete this.observer;
      }
    });

    this.observer.observe(target.parentNode, { childList: true });
  }
}

window.DOMCleanup = DOMCleanup;

沒有線路的 JavaScript Interop 呼叫

本節僅適用於伺服器端應用程式。

SignalR 線路中斷連線之後,無法發出 JavaScript (JS) Interop 呼叫。 在元件處置期間沒有線路,或在線路不存在的任何其他時間,下列方法呼叫會失敗,並以 JSDisconnectedException 記錄線路中斷連線的訊息:

為了避免記錄 JSDisconnectedException 或記錄自訂資訊,請在 try-catch 陳述式中攔截例外狀況。

針對下列元件處置範例:

  • 元件會實作 IAsyncDisposable
  • objInstanceIJSObjectReference
  • JSDisconnectedException 已攔截且未記錄。
  • 您可以選擇性地在 catch 陳述式中記錄自訂資訊,不論您偏好的記錄層級為何。 下列範例不會記錄自訂資訊,因為它假設開發人員不在乎元件處置期間線路中斷連線的時間或位置。
async ValueTask IAsyncDisposable.DisposeAsync()
{
    try
    {
        if (objInstance is not null)
        {
            await objInstance.DisposeAsync();
        }
    }
    catch (JSDisconnectedException)
    {
    }
}

如果您必須在線路遺失之後,在用戶端上清除自己的 JS 物件,或執行其他 JS 程式碼,請在用戶端上使用 JS 的 MutationObserver 模式。 MutationObserver 模式可讓您在從 DOM 移除元素時執行函式。

如需詳細資訊,請參閱下列文章:

快取的 JavaScript 檔案

Development 環境中開發期間,通常不會在用戶端上快取 JavaScript (JS) 檔案和其他靜態資產。 在開發期間,靜態資產要求包含Cache-Control 標頭,其值為 no-cachemax-age,並包含零的值 (0)。

Production環境中的生產期間,用戶端通常會快取 JS 檔案。

為了在瀏覽器中停用用戶端快取,開發人員通常會採用下列其中一種方法:

  • 在瀏覽器的開發人員工具主控台開啟時停用快取。 您可以在每個瀏覽器維護人員的開發人員工具文件中找到指引:
  • 對 Blazor 應用程式的任何網頁執行手動瀏覽器重新整理,以從伺服器重新載入 JS 檔案。 ASP.NET Core 的 HTTP 快取中介軟體一律會接受用戶端所傳送的有效無快取 Cache-Control 標頭

如需詳細資訊,請參閱

JavaScript Interop 呼叫的大小限制

本節僅適用於伺服器端應用程式中的互動式元件。 針對用戶端元件,架構不會限制 JavaScript (JS) Interop 輸入和輸出的大小。

針對伺服器端應用程式中的互動式元件,從用戶端傳遞資料到伺服器的 JS Interop 呼叫會受限於中樞方法允許的傳入 SignalR 訊息大小上限,此上限是由 HubOptions.MaximumReceiveMessageSize 強制設定 (預設:32 KB)。 JS 到 SignalR 的訊息大於 MaximumReceiveMessageSize 時會擲回錯誤。 該架構不會對從中樞到用戶端的 SignalR 訊息大小施加限制。 如需有關大小限制、錯誤訊息,以及處理訊息大小限制之指導的詳細資訊,請參閱 ASP.NET Core BlazorSignalR 指導

判斷應用程式執行的位置

如果應用程式需要知道 JS Interop 呼叫的程式碼在何處執行,請使用 OperatingSystem.IsBrowser 來判斷元件是否正在 WebAssembly 上的瀏覽器內容中執行。