ASP.NET Core Blazor を使用した JavaScript [JSImport]/[JSExport] 相互運用

Note

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

この記事では、.NET 7 以降を採用するアプリ向けにリリースされた JavaScript (JS) [JSImport]/[JSExport] 相互運用 API を使用して、 クライアント側コンポーネントで JtaavaScript (JS) と対話する方法について説明します。

Blazor は、IJSRuntime インターフェイスに基づく独自の JS 相互運用メカニズムを提供します。 Blazor の JS 相互運用機能は、Blazor レンダー モードと Blazor Hybrid アプリ間で一様にサポートされています。 また、IJSRuntime は、ライブラリ作成者が Blazor エコシステム全体で共有するための JS 相互運用ライブラリを構築することも可能にしており、引き続き Blazor での JS 相互運用に推奨されるアプローチです。 次の記事をご覧ください。

この記事では、WebAssembly で実行されるクライアント側コンポーネントに固有の、代替の JS 相互運用アプローチについて説明します。 これらのアプローチは、クライアント側の WebAssembly でのみ実行することを想定している場合に適しています。 ライブラリ作成者は、これらのアプローチを使用して、コード実行中にアプリがブラウザーの WebAssembly で実行されているかどうかを確認する (OperatingSystem.IsBrowser) ことで JS 相互運用を最適化できます。 この記事で説明している方法は、.NET 7 以降に移行するときに、古いマーシャリング解除された JS 相互運用 API を置き換えるために使う必要があります。

Note

この記事では、クライアント側コンポーネントの 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();
    }
}

Note

JS 相互運用がクライアントにレンダリングされたコンポーネントによってのみ呼び出されるように、OperatingSystem.IsBrowser を使用してコードに条件付きチェックを含めます。 これは、サーバー側コンポーネントを対象とするライブラリ/NuGet パッケージにとって重要です。サーバー側コンポーネントはこの JS 相互運用 API によって提供されるコードを実行できません。

C# からそれを呼び出す JS 関数をインポートするには、JS 関数のシグネチャに一致する C# メソッド シグネチャで [JSImport] 属性を使用します。 [JSImport] 属性の最初のパラメーターは、インポートする JS 関数の名前で、2 番目のパラメーターは JS モジュールの名前です。

次の例で、getMessage は、CallJavaScript1 というモジュールの string を返す JS 関数です。 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> を使用します。 たとえば、longSystem.Runtime.InteropServices.JavaScript.JSType.Number または System.Runtime.InteropServices.JavaScript.JSType.BigInt としてマーシャリングするように選択できます。 Action/Func<TResult> コールバックをパラメーターとして渡すことができます。これは呼び出し可能な JS 関数としてマーシャリングされます。 JS とマネージド オブジェクト参照の両方を渡すことができ、それらはプロキシ オブジェクトとしてマーシャリングされ、プロキシがガベージ コレクションされるまでオブジェクトが境界を越えて維持されます。 Task の結果によって非同期メソッドをインポートおよびエクスポートすることもできます。これは JS Promise としてマーシャリングされます。 マーシャリングされた型のほとんどは、インポートされたメソッドとエクスポートされたメソッドの両方で、パラメーターとしても戻り値としても、両方向で動作します。これについては、この記事の後半の「JavaScript からの .NET の呼び出し」セクションで説明しています。

次の表に、サポートされている型マッピングを示します。

.NET JavaScript Nullable TaskからPromise 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 としてマーシャリングできるかどうかが示されます。 例: Number の JSArray にマップされた C#int[] (Int32)。
  • 間違った型の値を持つ JS 値を C# に渡すと、ほとんどの場合、フレームワークによって例外がスローされます。 フレームワークによって、JS でコンパイル時の型チェックは実行されません。
  • JSObjectExceptionTaskArraySegment では GCHandle とプロキシが作成されます。 開発者コードで破棄をトリガーしたり、.NET ガベージ コレクション (GC) でオブジェクトを後で破棄したりすることができます。 これらの型では、パフォーマンスに大きなオーバーヘッドが伴います。
  • Array: 配列をマーシャリングすると、JS または .NET に配列のコピーが作成されます。
  • MemoryView
    • MemoryView は、.NET WebAssembly ランタイムが SpanArraySegment をマーシャリングするための JS クラスです。
    • 配列のマーシャリングとは異なり、SpanArraySegment をマーシャリングしても、基になるメモリのコピーは作成されません。
    • MemoryView は、.NET WebAssembly ランタイムによってのみ適切にインスタンス化されます。 したがって、Span または ArraySegment のパラメーターを持つ .NET メソッドとして JS 関数をインポートすることはできません。
    • Span に対して作成された MemoryView は、相互運用呼び出しの期間のみ有効です。 Span は呼び出し履歴に割り当てられ、相互運用呼び出しの後に保持されないため、Span を返す .NET メソッドをエクスポートすることはできません。
    • ArraySegment に対して作成された MemoryView は、相互運用呼び出し後も存続し、バッファーを共有するのに役立ちます。 ArraySegment に対して作成された MemoryViewdispose() を呼び出すと、プロキシが破棄され、基になる .NET 配列の固定が解除されます。 MemoryViewtry-finally ブロックで dispose() を呼び出すことをお勧めします。

次の表に、サポートされている型マッピングを示します。

.NET JavaScript Nullable TaskからPromise 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 としてマーシャリングできるかどうかが示されます。 例: Number の JSArray にマップされた C#int[] (Int32)。
  • 間違った型の値を持つ JS 値を C# に渡すと、ほとんどの場合、フレームワークによって例外がスローされます。 フレームワークによって、JS でコンパイル時の型チェックは実行されません。
  • JSObjectExceptionTaskArraySegment では GCHandle とプロキシが作成されます。 開発者コードで破棄をトリガーしたり、.NET ガベージ コレクション (GC) でオブジェクトを後で破棄したりすることができます。 これらの型では、パフォーマンスに大きなオーバーヘッドが伴います。
  • Array: 配列をマーシャリングすると、JS または .NET に配列のコピーが作成されます。
  • MemoryView
    • MemoryView は、.NET WebAssembly ランタイムが SpanArraySegment をマーシャリングするための JS クラスです。
    • 配列のマーシャリングとは異なり、SpanArraySegment をマーシャリングしても、基になるメモリのコピーは作成されません。
    • MemoryView は、.NET WebAssembly ランタイムによってのみ適切にインスタンス化されます。 したがって、Span または ArraySegment のパラメーターを持つ .NET メソッドとして JS 関数をインポートすることはできません。
    • Span に対して作成された MemoryView は、相互運用呼び出しの期間のみ有効です。 Span は呼び出し履歴に割り当てられ、相互運用呼び出しの後に保持されないため、Span を返す .NET メソッドをエクスポートすることはできません。
    • ArraySegment に対して作成された MemoryView は、相互運用呼び出し後も存続し、バッファーを共有するのに役立ちます。 ArraySegment に対して作成された MemoryViewdispose() を呼び出すと、プロキシが破棄され、基になる .NET 配列の固定が解除されます。 MemoryViewtry-finally ブロックで dispose() を呼び出すことをお勧めします。

[JSImport] 属性のモジュール名と、JSHost.ImportAsync を使用してコンポーネントにモジュールを読み込む呼び出しは一致していて、アプリで一意である必要があります。 NuGet パッケージでデプロイ用のライブラリを作成する場合は、モジュール名のプレフィックスとして NuGet パッケージ名前空間を使用することが推奨されます。 次の例では、モジュール名は Contoso.InteropServices.JavaScript パッケージとユーザー メッセージ相互運用クラス (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);

JS ファイル内でコンポーネントと併置されているか、他の JavaScript 静的資産と共に配置されている標準 JavaScript ES6 モジュールからスクリプトをエクスポートします (たとえば wwwroot/js/{FILE NAME}.js。ここで JS 静的資産はアプリの wwwroot フォルダー内の js というフォルダーに保持され、{FILE NAME} プレースホルダーはファイル名です)。

次の例では、getMessage という JS 関数が、ポルトガル語で "Hello from Blazor!" というウェルカム メッセージを返す、併置された JS ファイルからエクスポートされます。

CallJavaScript1.razor.js:

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

JavaScript から .NET を呼び出す

このセクションでは、JS から .NET メソッドを呼び出す方法について説明します。

次の CallDotNet1 コンポーネントは、DOM と直接対話してウェルカム メッセージ文字列をレンダリングする JS を呼び出します。

  • CallDotNetJS モジュールは、このコンポーネントの併置された JS ファイルから非同期的にインポートされます。
  • インポートされた setMessageJS 関数が、SetWelcomeMessage によって呼び出されます。
  • 返されたウェルカム メッセージが、setMessage によって、message フィールドを介して UI に表示されます。

重要

このセクションの例では、OnAfterRender でコンポーネントがレンダリングされた後に、純粋にデモンストレーション目的で DOM 要素を変更するために、JS 相互運用を使用しています。 一般に、JS によって DOM を変更する必要があるのは、オブジェクトが Blazor と対話しない場合だけです。 このセクションに示すアプローチは、Razor コンポーネントでサードパーティ JS ライブラリを使用する場合と似ています。コンポーネントは JS 相互運用を介して JS ライブラリと対話し、サードパーティ JS ライブラリは DOM の一部と対話し、Blazor は DOM のその部分への DOM の更新に直接関与しません。 詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (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 は、ポルトガル語のウェルカム メッセージ "Hello from Blazor!" を返す [JSExport] 属性を持つ .NET メソッドです。

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> テキストに割り当てられます。 アプリの名前空間は 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) を呼び出して、エクスポートされた .NET メソッドを呼び出すための WebAssembly .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 モジュールを使用する

このセクションのガイダンスに従う前に、[JSImport]/[JSExport] 相互運用に関する一般的なガイダンスを提供している、この記事の「.NET から JavaScript を呼び出す」と「JavaScript から .NET を呼び出す」セクションを参照してください。

このセクションの例では、クライアント側アプリで共有 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();
}

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();
        }
    }
}

重要

このセクションの例では、OnAfterRender でコンポーネントがレンダリングされた後に、純粋にデモンストレーション目的で DOM 要素を変更するために、JS 相互運用を使用しています。 一般に、JS によって DOM を変更する必要があるのは、オブジェクトが Blazor と対話しない場合だけです。 このセクションに示すアプローチは、Razor コンポーネントでサードパーティ JS ライブラリを使用する場合と似ています。コンポーネントは JS 相互運用を介して JS ライブラリと対話し、サードパーティ JS ライブラリは DOM の一部と対話し、Blazor は DOM のその部分への DOM の更新に直接関与しません。 詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」をご覧ください。

その他のリソース