ASP.NET Core Blazor의 JavaScript 함수에서 .NET 메서드 호출

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 ASP.NET Core 8.0 버전을 참조 하세요.

이 문서에서는 JavaScript(JS)에서 .NET 메서드를 호출하는 방법을 설명합니다.

.NET에서 JS 함수를 호출하는 방법에 대한 자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.

이 문서 전체에서 서버 서버/쪽 및 클라이언트/클라이언트 쪽이라는 용어는 앱 코드가 실행되는 위치를 구분하는 데 사용됩니다.

  • 서버/서버 쪽: 웹앱의 Blazor 대화형 서버 쪽 렌더링(대화형 SSR)
  • 클라이언트/클라이언트 쪽
    • 웹앱의 CSR(클라이언트 쪽 렌더링)입니다 Blazor .
    • 앱입니다 Blazor WebAssembly .

설명서 구성 요소 예제는 일반적으로 구성 요소의 정의 파일(.razor)에 지시문을 사용하여 @rendermode 대화형 렌더링 모드를 구성하지 않습니다.

  • Blazor 웹앱에서 구성 요소는 구성 요소의 정의 파일에 적용되거나 부모 구성 요소에서 상속된 대화형 렌더링 모드가 있어야 합니다. 자세한 내용은 ASP.NET Core Blazor 렌더링 모드를 참조하세요.

  • 독립 실행형 Blazor WebAssembly 앱에서 구성 요소는 표시된 대로 작동하며 구성 요소가 항상 앱의 WebAssembly에서 대화형으로 실행되므로 렌더링 모드가 Blazor WebAssembly 필요하지 않습니다.

Interactive WebAssembly 또는 대화형 자동 렌더링 모드를 사용하는 경우 클라이언트로 전송된 구성 요소 코드를 디컴파일하고 검사할 수 있습니다. 프라이빗 코드, 앱 비밀 또는 기타 중요한 정보를 클라이언트 렌더링 구성 요소에 배치하지 마세요.

  • 서버/서버 쪽
    • Server 호스트 Blazor WebAssembly 된 앱의 프로젝트입니다.
    • 앱입니다 Blazor Server .
  • 클라이언트/클라이언트 쪽
    • Client 호스트 Blazor WebAssembly 된 앱의 프로젝트입니다.
    • 앱입니다 Blazor WebAssembly .

파일 및 폴더의 용도 및 위치에 대한 지침은 ASP.NET Core Blazor 프로젝트 구조를 참조하세요. 이 구조에서는 시작 스크립트의 Blazor 위치와 콘텐츠<body><head> 위치도 설명합니다.

데모 코드를 실행하는 가장 좋은 방법은 대상으로 하는 .NET 버전과 일치하는 샘플 GitHub 리포지토리에서Blazor샘플 앱을 다운로드 BlazorSample_{PROJECT TYPE} 하는 것입니다. 현재 모든 설명서 예제가 샘플 앱에 있는 것은 아니지만 대부분의 .NET 8 문서 예제를 .NET 8 샘플 앱으로 이동하기 위한 노력이 현재 진행 중입니다. 이 작업은 2024년 1분기에 완료될 예정입니다.

정적 .NET 메서드 호출

JavaScript(JS)에서 정적 .NET 메서드를 호출하려면 JS 함수를 사용합니다.

  • DotNet.invokeMethodAsync (권장): 서버 쪽 및 클라이언트 쪽 구성 요소 모두에 대한 비동기입니다.
  • DotNet.invokeMethod: 클라이언트 쪽 구성 요소에 대해서만 동기식입니다.

메서드를 포함하는 어셈블리의 이름, 정적 .NET 메서드 식별자 및 인수를 전달합니다.

다음 예제에서

  • {ASSEMBLY NAME} 자리 표시자는 앱의 어셈블리 이름입니다.
  • {.NET METHOD ID} 자리 표시자는 .NET 메서드 식별자입니다.
  • {ARGUMENTS} 자리 표시자는 메서드에 전달할 쉼표로 구분된 선택적 인수이며 각 인수는 JS직렬화 가능해야 합니다.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethodAsyncJS Promise 작업의 결과를 나타내는 값을 반환합니다. DotNet.invokeMethod (클라이언트 쪽 구성 요소)는 작업의 결과를 반환합니다.

Important

서버 쪽 구성 요소의 경우 동기 버전()에 대해 비동기 함수(invokeMethodAsyncinvokeMethod)를 사용하는 것이 좋습니다.

.NET 메서드는 퍼블릭이고 정적이며 [JSInvokable] 특성이 있어야 합니다.

다음 예제에서

  • {<T>} 자리 표시자는 값을 반환하는 메서드에만 필요한 반환 형식을 나타냅니다.
  • {.NET METHOD ID} 자리 표시자는 메서드 식별자입니다.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

참고 항목

개방형 제네릭 메서드 호출은 정적 .NET 메서드에서 지원되지 않지만, 인스턴스 메서드에서는 지원됩니다. 자세한 내용은 .NET 제네릭 클래스 메서드 호출 섹션을 참조하세요.

다음 구성 요소에서 C# 메서드는 ReturnArrayAsync 배열을 반환합니다 int . [JSInvokable] 특성은 메서드에 적용되어 메서드를 JS에서 호출 가능하게 만듭니다.

CallDotnet1.razor:

@page "/call-dotnet-1"

<PageTitle>Call .NET 1</PageTitle>

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

<p>
    See the result in the developer tools console.
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

<button> 요소의 onclick HTML 특성은 Blazor의 @onclick 지시문 특성이 아니라 click 이벤트를 처리하기 위한 JavaScript의 onclick 이벤트 처리기 할당입니다. returnArrayAsyncJS 함수는 처리기로서 할당됩니다.

다음 returnArrayAsyncJS 함수는 이전 구성 요소의 .NET 메서드를 호출 ReturnArrayAsync 하고 결과를 브라우저의 웹 개발자 도구 콘솔에 기록합니다. BlazorSample은 앱의 어셈블리 이름입니다.

<script>
  window.returnArrayAsync = () => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        console.log(data);
      });
    };
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

Trigger .NET static method 단추를 선택하면 브라우저의 개발자 도구 콘솔 출력이 배열 데이터를 표시합니다. 출력 형식은 브라우저마다 약간씩 다릅니다. 다음 출력은 Microsoft Edge에서 사용하는 형식을 보여 줍니다.

Array(3) [ 1, 2, 3 ]

함수를 호출 invokeMethodAsync 할 때 데이터를 인수로 전달하여 .NET 메서드에 데이터를 전달합니다.

.NET에 데이터를 전달하는 방법을 보여 주려면 함수가 호출될 때 이전 returnArrayAsyncJS 함수가 시작 위치를 수신하도록 하고 값을 함수에 invokeMethodAsync 인수로 전달합니다.

<script>
  window.returnArrayAsync = (startPosition) => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync', startPosition)
      .then(data => {
        console.log(data);
      });
    };
</script>

구성 요소에서 시작 위치를 포함하도록 함수 호출을 변경합니다. 다음 예제에서는 다음 값을 5사용합니다.

<button onclick="returnArrayAsync(5)">
    ...
</button>

구성 요소의 호출 가능한 ReturnArrayAsync 메서드는 시작 위치를 수신하고 해당 위치에서 배열을 생성합니다. 배열은 콘솔에 로깅하기 위해 반환됩니다.

[JSInvokable]
public static Task<int[]> ReturnArrayAsync(int startPosition)
{
    return Task.FromResult(Enumerable.Range(startPosition, 3).ToArray());
}

앱을 다시 컴파일하고 브라우저를 새로 고친 후 단추를 선택하면 브라우저 콘솔에 다음 출력이 표시됩니다.

Array(3) [ 5, 6, 7 ]

기본적으로 JS 호출의 .NET 메서드 식별자는 .NET 메서드 이름이지만 [JSInvokable] 특성 생성자를 사용하여 다른 식별자를 지정할 수 있습니다. 다음 예제에서 DifferentMethodNameReturnArrayAsync 메서드에 대해 할당된 메서드 식별자입니다.

[JSInvokable("DifferentMethodName")]

(서버 쪽 또는 클라이언트 쪽 구성 요소) 또는 DotNet.invokeMethod (클라이언트 쪽 구성 요소에만 해당)에 대한 호출 DotNet.invokeMethodAsync 에서 .NET 메서드를 ReturnArrayAsync 실행하기 위한 호출DifferentMethodName:

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (클라이언트 쪽 구성 요소만 해당)

참고 항목

이 단원의 ReturnArrayAsync 메서드 예제에서는 명시적 C# asyncawait 키워드를 사용하지 않고 Task의 결과를 반환합니다. asyncawait를 사용하여 메서드를 코딩하는 방식은 await 키워드를 사용하여 비동기 작업의 값을 반환하는 메서드에서 일반적입니다.

asyncawait 키워드로 구성된 ReturnArrayAsync 메서드:

[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
    return await Task.FromResult(new int[] { 1, 2, 3 });
}

자세한 내용은 C# 가이드에서 async 및 await를 사용한 비동기 프로그래밍을 참조하세요.

.NET에 전달할 JavaScript 개체 및 데이터 참조 만들기

개체 참조를 JS 만드는 데 사용되는 .NET jsObject 에 전달할 수 있도록 개체 참조를 JS 생성하는 호출 DotNet.createJSObjectReference(jsObject) 입니다JS Object. 다음 예제에서는 직렬화할 수 없는 window 개체에 대한 참조를 .NET에 전달합니다. 이 개체는 ReceiveWindowObject C# 메서드에서 이를 IJSObjectReference로 받습니다.

DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', 
  DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
    ...
}

앞의 예시에서 {ASSEMBLY NAME} 자리 표시자는 앱의 네임스페이스입니다.

참고 항목

앞의 예제에서는 개체에 JSObjectReference대한 참조 window 가 저장 JS되지 않으므로 삭제할 필요가 없습니다.

참조 JSObjectReference 를 유지 관리하려면 클라이언트에서 메모리가 누출 JS 되는 것을 방지하기 위해 삭제해야 합니다. 다음 예제에서는 참조를 캡처 JSObjectReference하기 위해 앞의 코드를 리팩터링한 다음 참조를 삭제하기 위한 DotNet.disposeJSObjectReference() 호출을 수행합니다.

var jsObjectReference = DotNet.createJSObjectReference(window);

DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);

DotNet.disposeJSObjectReference(jsObjectReference);

앞의 예시에서 {ASSEMBLY NAME} 자리 표시자는 앱의 네임스페이스입니다.

DotNet.createJSStreamReference(streamReference)을 호출하여 JS 스트림 참조를 .NET에 전달할 수 있도록 합니다. 여기서 streamReferenceArrayBuffer, Blob 또는 JS 스트림 참조를 만드는 데 사용되는 형식화된 배열(예: Uint8Array 또는 Float32Array)입니다.

인스턴스 .NET 메서드 호출

JavaScript(JS)에서 인스턴스 .NET 메서드를 호출하려면

  • DotNetObjectReference에서 인스턴스를 래핑하고 Create를 호출하여 .NET 인스턴스를 JS에 대한 참조로 전달합니다.

  • 전달DotNetObjectReference된 인스턴스에서 JS .NET 인스턴스 메서드를 사용하여 invokeMethodAsync (권장) 또는 invokeMethod (클라이언트 쪽 구성 요소만) 호출합니다. 인스턴스 .NET 메서드 및 인수의 식별자를 전달합니다. JS에서 다른 .NET 메서드를 호출할 때 .NET 인스턴스를 인수로 전달할 수도 있습니다.

    다음 예제에서

    • dotNetHelper 는 .입니다 DotNetObjectReference.
    • {.NET METHOD ID} 자리 표시자는 .NET 메서드 식별자입니다.
    • {ARGUMENTS} 자리 표시자는 메서드에 전달할 쉼표로 구분된 선택적 인수이며 각 인수는 JS직렬화 가능해야 합니다.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    참고 항목

    invokeMethodAsync 인스턴스 invokeMethod 메서드를 호출할 때 어셈블리 이름 매개 변수를 허용하지 않습니다.

    invokeMethodAsyncJS Promise 작업의 결과를 나타내는 값을 반환합니다. invokeMethod (클라이언트 쪽 구성 요소만) 작업의 결과를 반환합니다.

    Important

    서버 쪽 구성 요소의 경우 동기 버전()에 대해 비동기 함수(invokeMethodAsyncinvokeMethod)를 사용하는 것이 좋습니다.

  • DotNetObjectReference를 삭제합니다.

이 문서의 다음 섹션에서는 인스턴스 .NET 메서드를 호출하는 다양한 방법을 보여 줍니다.

JavaScript 호출 가능한 .NET 메서드를 트리밍하지 않습니다.

이 섹션은 AOT(미리 실행) 컴파일런타임 다시 연결을 사용하도록 설정된 클라이언트 쪽 앱에 적용됩니다.

다음 섹션의 몇 가지 예제는 클래스 인스턴스 접근 방식을 기반으로 합니다. 여기서 특성으로 [JSInvokable] 표시된 JavaScript 호출 가능한 .NET 메서드는 구성 요소가 아닌 Razor 클래스의 멤버입니다. 이러한 .NET 메서드가 구성 요소에 Razor 있는 경우 런타임 다시 연결/트리밍으로부터 보호됩니다. .NET 메서드가 구성 요소 외부에서 Razor 트리밍되는 것을 방지하려면 다음 예제와 DynamicDependency 같이 클래스 생성자의 특성을 사용하여 메서드를 구현합니다.

using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;

public class ExampleClass {

    [DynamicDependency(nameof(ExampleJSInvokableMethod))]
    public ExampleClass()
    {
    }

    [JSInvokable]
    public string ExampleJSInvokableMethod()
    {
        ...
    }
}

자세한 내용은 트리밍을 위한 .NET 라이브러리 준비: DynamicDependency를 참조 하세요.

개별 JavaScript 함수에 DotNetObjectReference 전달

이 섹션의 예제에서는 개별 JS(JavaScript) 함수에 DotNetObjectReference를 전달하는 방법을 보여 줍니다.

다음 sayHello1JS 함수는 DotNetObjectReference를 수신하고 invokeMethodAsync를 호출하여 구성 요소의 GetHelloMessage .NET 메서드를 호출합니다.

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

앞의 예제에서 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

다음 구성 요소:

  • 구성 요소에는 GetHelloMessage라는 JS 호출 가능 .NET 메서드가 있습니다.
  • Trigger .NET instance method 단추를 선택하면 JS 함수 sayHello1DotNetObjectReference를 사용하여 호출됩니다.
  • sayHello1:
    • GetHelloMessage를 호출하고 메시지 결과를 받습니다.
    • 호출하는 TriggerDotNetInstanceMethod 메서드에 메시지 결과를 반환합니다.
  • resultsayHello1에서 반환된 메시지는 사용자에게 표시됩니다.
  • 메모리 누수를 방지하고 가비지 수집을 허용하기 위해 DotNetObjectReference에서 만든 .NET 개체 참조는 Dispose 메서드에서 삭제됩니다.

CallDotnet2.razor:

@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET 2</PageTitle>

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotnet2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose() => objRef?.Dispose();
}

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

앞의 예제에서 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

다음 지침을 사용하여 인스턴스 메서드에 인수를 전달합니다.

.NET 메서드 호출에 매개 변수를 추가합니다. 다음 예제에서는 이름이 메서드에 전달됩니다. 필요에 따라 목록에 추가 매개 변수를 추가합니다.

<script>
  window.sayHello2 = (dotNetHelper, name) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
  };
</script>

앞의 예제에서 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

.NET 메서드에 매개 변수 목록을 제공합니다.

CallDotnet3.razor:

@page "/call-dotnet-3"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET 3</PageTitle>

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotnet3>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

    public void Dispose() => objRef?.Dispose();
}

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample3>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample3>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample3> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample3> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

앞의 예제에서 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

여러 JavaScript 함수가 있는 클래스에 DotNetObjectReference 전달

이 섹션의 예제에서는 여러 함수가 있는 JS(JavaScript) 클래스에 DotNetObjectReference를 전달하는 방법을 보여 줍니다.

DotNetObjectReference를 만들고 OnAfterRenderAsync 수명 주기 메서드에서 사용할 여러 함수의 JS 클래스로 전달합니다. 다음 예제와 같이 .NET 코드가 DotNetObjectReference를 삭제하는지 확인합니다.

다음 구성 요소에서 단추는 Trigger JS function 지시문 특성이 JSonclick 아닌Blazor 속성을 설정하여 함수를 @onclick 호출 JS 합니다.

CallDotNetExampleOneHelper.razor:

@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET Example</PageTitle>

<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>

<p>
    <label>
        Message: <input @bind="name" />
    </label>
</p>

<p>
    <button onclick="GreetingHelpers.sayHello()">
        Trigger JS function <code>sayHello</code>
    </button>
</p>

<p>
    <button onclick="GreetingHelpers.welcomeVisitor()">
        Trigger JS function <code>welcomeVisitor</code>
    </button>
</p>

@code {
    private string? name;
    private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetHelper = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", 
                dotNetHelper);
        }
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    [JSInvokable]
    public string GetWelcomeMessage() => $"Welcome, {name}!";

    public void Dispose()
    {
        dotNetHelper?.Dispose();
    }
}
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET Example</PageTitle>

<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>

<p>
    <label>
        Message: <input @bind="name" />
    </label>
</p>

<p>
    <button onclick="GreetingHelpers.sayHello()">
        Trigger JS function <code>sayHello</code>
    </button>
</p>

<p>
    <button onclick="GreetingHelpers.welcomeVisitor()">
        Trigger JS function <code>welcomeVisitor</code>
    </button>
</p>

@code {
    private string? name;
    private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetHelper = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", 
                dotNetHelper);
        }
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    [JSInvokable]
    public string GetWelcomeMessage() => $"Welcome, {name}!";

    public void Dispose()
    {
        dotNetHelper?.Dispose();
    }
}
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET Example</PageTitle>

<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>

<p>
    <label>
        Message: <input @bind="name" />
    </label>
</p>

<p>
    <button onclick="GreetingHelpers.sayHello()">
        Trigger JS function <code>sayHello</code>
    </button>
</p>

<p>
    <button onclick="GreetingHelpers.welcomeVisitor()">
        Trigger JS function <code>welcomeVisitor</code>
    </button>
</p>

@code {
    private string name;
    private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetHelper = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", 
                dotNetHelper);
        }
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    [JSInvokable]
    public string GetWelcomeMessage() => $"Welcome, {name}!";

    public void Dispose()
    {
        if (dotNetHelper is not null)
        {
            dotNetHelper.Dispose();
        }
    }
}

앞의 예에서:

  • JS는 삽입된 IJSRuntime 인스턴스입니다. IJSRuntime은 Blazor 프레임워크에 의해 등록됩니다.
  • 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.
  • 구성 요소는 가비지 수집을 허용하고 메모리 누수 방지를 위해 DotNetObjectReference를 명시적으로 삭제해야 합니다.
<script>
  class GreetingHelpers {
    static dotNetHelper;

    static setDotNetHelper(value) {
      GreetingHelpers.dotNetHelper = value;
    }

    static async sayHello() {
      const msg = 
        await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
      alert(`Message from .NET: "${msg}"`);
    }

    static async welcomeVisitor() {
      const msg = 
        await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
      alert(`Message from .NET: "${msg}"`);
    }
  }
    
  window.GreetingHelpers = GreetingHelpers;
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

앞의 예에서:

  • GreetingHelpers 클래스는 Blazor가 JS interop에 대한 클래스를 찾을 수 있도록 하는 클래스를 전역적으로 정의하기 위해 window 개체에 추가됩니다.
  • 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

.NET 제네릭 클래스 메서드 호출

JS(JavaScript) 함수는 .NET 제네릭 클래스 메서드를 호출할 수 있습니다. 여기서 JS 함수는 제네릭 클래스의 .NET 메서드를 호출합니다.

다음 제네릭 형식 클래스(GenericType<TValue>)에서:

  • 클래스에는 단일 제네릭 Value 속성이 있는 단일 형식 매개 변수(TValue)가 있습니다.
  • 클래스에는 특성으로 표시된 두 개의 제네릭이 아닌 메서드가 [JSInvokable] 있으며, 각각 제네릭 형식 매개 변수가 다음과 같습니다newValue.
    • UpdatenewValueValue 값을 동기적으로 업데이트합니다.
    • UpdateAsync는 대기할 때 현재 컨텍스트로 비동기적으로 전환되는 Task.Yield로 대기할 수 있는 작업을 만든 후 newValueValue 값을 비동기적으로 업데이트합니다.
  • 각 클래스 메서드는 콘솔에 TValue 형식 및 Value 값을 씁니다. 콘솔에 쓰는 것은 데모용으로만 제공됩니다. 프로덕션 앱은 일반적으로 앱 로깅을 위해 콘솔에 쓰지 않습니다. 자세한 내용은 ASP.NET Core Blazor 로깅.NET Core 및 ASP.NET Core의 로깅을 참조하세요.

참고 항목

열린 제네릭 형식 및 메서드 는 형식 자리 표시자에 대한 형식을 지정하지 않습니다. 반대로 닫힌 제네릭은 모든 형식 자리 표시자에 대해 형식을 제공합니다. 이 섹션의 예제에서는 닫힌 제네릭을 보여 주지만 개방형 제네릭을 사용하여 interop 인스턴스 메서드를 호출하는 JS 것이 지원됩니다. 이 문서의 앞부분에서 설명한 것처럼 정적 .NET 메서드 호출에는 개방형 제너릭 사용이 지원되지 않습니다.

자세한 내용은 다음 문서를 참조하세요.

GenericType.cs:

using Microsoft.JSInterop;

public class GenericType<TValue>
{
    public TValue? Value { get; set; }

    [JSInvokable]
    public void Update(TValue newValue)
    {
        Value = newValue;

        Console.WriteLine($"Update: GenericType<{typeof(TValue)}>: {Value}");
    }

    [JSInvokable]
    public async void UpdateAsync(TValue newValue)
    {
        await Task.Yield();
        Value = newValue;

        Console.WriteLine($"UpdateAsync: GenericType<{typeof(TValue)}>: {Value}");
    }
}

다음 invokeMethodsAsync 함수에서

  • 제네릭 형식 클래스의 UpdateUpdateAsync 메서드는 문자열과 숫자를 나타내는 인수를 사용하여 호출됩니다.
  • 클라이언트 쪽 구성 요소는 .NET 메서드를 .NET 메서드와 invokeMethod동기적으로 호출할 수 있습니다. syncInterop 는 클라이언트에서 interop이 JS 발생하는지 여부를 나타내는 부울 값을 받습니다. syncInteroptrue인 경우 invokeMethod는 안전하게 호출됩니다. 값 syncInterop 이면 falseinterop이 서버 쪽 구성 요소에서 실행되므로 비동기 함수 invokeMethodAsync 만 호출 JS 됩니다.
  • 데모용으로 DotNetObjectReference 함수 호출(invokeMethod 또는 invokeMethodAsync), 호출된 .NET 메서드(Update 또는 UpdateAsync), 인수가 콘솔에 기록됩니다. 인수는 난수를 사용하여 .NET 메서드 호출에 JS 함수 호출을 일치시키도록 허용합니다(.NET 쪽의 콘솔에도 기록됨). 프로덕션 코드는 일반적으로 클라이언트 또는 서버의 콘솔에 쓰지 않습니다. 프로덕션 앱은 일반적으로 앱 로깅을 사용합니다. 자세한 내용은 ASP.NET Core Blazor 로깅.NET Core 및 ASP.NET Core의 로깅을 참조하세요.
<script>
  const randomInt = () => Math.floor(Math.random() * 99999);

  window.invokeMethodsAsync = async (syncInterop, dotNetHelper1, dotNetHelper2) => {
    var n = randomInt();
    console.log(`JS: invokeMethodAsync:Update('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('Update', `string ${n}`);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('UpdateAsync', `string ${n}`);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update('string ${n}')`);
      dotNetHelper1.invokeMethod('Update', `string ${n}`);
    }

    n = randomInt();
    console.log(`JS: invokeMethodAsync:Update(${n})`);
    await dotNetHelper2.invokeMethodAsync('Update', n);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync(${n})`);
    await dotNetHelper2.invokeMethodAsync('UpdateAsync', n);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update(${n})`);
      dotNetHelper2.invokeMethod('Update', n);
    }
  };
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

다음은 GenericsExample 구성 요소에 대한 설명입니다.

  • Invoke Interop 단추가 선택되면 JS 함수 invokeMethodsAsync가 호출됩니다.
  • DotNetObjectReference 형식 쌍이 만들어지고 GenericType 인스턴스에 대한 JS 함수에 stringint로 전달됩니다.

GenericsExample.razor:

@page "/generics-example"
@using System.Runtime.InteropServices
@implements IDisposable
@inject IJSRuntime JS

<p>
    <button @onclick="InvokeInterop">Invoke Interop</button>
</p>

<ul>
    <li>genericType1: @genericType1?.Value</li>
    <li>genericType2: @genericType2?.Value</li>
</ul>

@code {
    private GenericType<string> genericType1 = new() { Value = "string 0" };
    private GenericType<int> genericType2 = new() { Value = 0 };
    private DotNetObjectReference<GenericType<string>>? objRef1;
    private DotNetObjectReference<GenericType<int>>? objRef2;

    protected override void OnInitialized()
    {
        objRef1 = DotNetObjectReference.Create(genericType1);
        objRef2 = DotNetObjectReference.Create(genericType2);
    }

    public async Task InvokeInterop()
    {
        var syncInterop =
            RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));

        await JS.InvokeVoidAsync(
            "invokeMethodsAsync", syncInterop, objRef1, objRef2);
    }

    public void Dispose()
    {
        objRef1?.Dispose();
        objRef2?.Dispose();
    }
}
@page "/generics-example"
@using System.Runtime.InteropServices
@implements IDisposable
@inject IJSRuntime JS

<p>
    <button @onclick="InvokeInterop">Invoke Interop</button>
</p>

<ul>
    <li>genericType1: @genericType1?.Value</li>
    <li>genericType2: @genericType2?.Value</li>
</ul>

@code {
    private GenericType<string> genericType1 = new() { Value = "string 0" };
    private GenericType<int> genericType2 = new() { Value = 0 };
    private DotNetObjectReference<GenericType<string>>? objRef1;
    private DotNetObjectReference<GenericType<int>>? objRef2;

    protected override void OnInitialized()
    {
        objRef1 = DotNetObjectReference.Create(genericType1);
        objRef2 = DotNetObjectReference.Create(genericType2);
    }

    public async Task InvokeInterop()
    {
        var syncInterop =
            RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));

        await JS.InvokeVoidAsync(
            "invokeMethodsAsync", syncInterop, objRef1, objRef2);
    }

    public void Dispose()
    {
        objRef1?.Dispose();
        objRef2?.Dispose();
    }
}

앞의 예제에서 JS은(는) 삽입된 IJSRuntime 인스턴스입니다. IJSRuntime은 Blazor 프레임워크에 의해 등록됩니다.

다음은 클라이언트 쪽 구성 요소에서 단추를 선택할 때 이전 예제의 Invoke Interop 일반적인 출력을 보여 줍니다.

JS: invokeMethodAsync:Update('string 37802')
.NET: Update: GenericType<System.String>: string 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: Update: GenericType<System.String>: string 26784
JS: invokeMethodAsync:Update(14107)
.NET: Update: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invokeMethod:Update(12872)
.NET: Update: GenericType<System.Int32>: 12872
.NET: UpdateAsync: GenericType<System.String>: string 53051
.NET: UpdateAsync: GenericType<System.Int32>: 48995

앞의 예제가 서버 쪽 구성 요소에서 구현되는 경우 동기 호출 invokeMethod 은 방지됩니다. 서버 쪽 구성 요소의 경우 동기 버전()에 대해 비동기 함수(invokeMethodAsyncinvokeMethod)를 사용하는 것이 좋습니다.

서버 쪽 구성 요소의 일반적인 출력:

JS: invokeMethodAsync:Update('string 34809')
.NET: Update: GenericType<System.String>: string 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Update: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: string 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652

앞의 출력 예제에서는 스레드 예약 및 메서드 실행 속도를 비롯한 여러 요인에 따라 비동기 메서드가 임의 순서로 실행되고 완료되는 것을 보여 줍니다. 비동기 메서드 호출의 완료 순서를 안정적으로 예측할 수는 없습니다.

클래스 인스턴스 예제

다음 sayHello1JS 함수는 다음 작업을 수행합니다.

  • 전달된 DotNetObjectReference에 대해 GetHelloMessage .NET 메서드를 호출합니다.
  • GetHelloMessage에서 sayHello1 호출자로 메시지를 반환합니다.
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

앞의 예제에서 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

다음 HelloHelper 클래스에는 GetHelloMessage이라는 JS 호출 가능 .NET 메서드가 있습니다. HelloHelper가 만들어지면 Name 속성의 이름이 GetHelloMessage에서 메시지를 반환하는 데 사용됩니다.

HelloHelper.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class HelloHelper(string? name)
{
    public string? Name { get; set; } = name ?? "No Name";

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string? name)
    {
        Name = name ?? "No Name";
    }

    public string? Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string? name)
    {
        Name = name ?? "No Name";
    }

    public string? Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}

다음 JsInteropClasses3 클래스의 CallHelloHelperGetHelloMessage 메서드는 HelloHelper의 새 인스턴스를 사용하여 JS 함수 sayHello1를 호출합니다.

JsInteropClasses3.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses3(IJSRuntime js)
{
    private readonly IJSRuntime js = js;

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}

메모리 누수 및 가비지 수집을 허용하기 위해 개체 참조가 using var 구문으로 범위를 벗어날 때, DotNetObjectReference에서 만든 .NET 개체 참조가 삭제됩니다.

Trigger .NET instance method 다음 구성 요소 JsInteropClasses3.CallHelloHelperGetHelloMessage 에서 단추를 선택하면 값name으로 호출됩니다.

CallDotnet4.razor:

@page "/call-dotnet-4"
@inject IJSRuntime JS

<PageTitle>Call .NET 4</PageTitle>

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private JsInteropClasses3? jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private JsInteropClasses3? jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private JsInteropClasses3? jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }
}

다음 그림은 Name 필드에 이름 Amy Pond가 있는 렌더링된 구성 요소를 보여 줍니다. 이 단추를 선택하면 Hello, Amy Pond!가 UI에 표시됩니다.

Rendered 'CallDotNetExample4' component example

JsInteropClasses3 클래스에 표시된 이전 패턴은 구성 요소에서 완전히 구현될 수도 있습니다.

CallDotnet5.razor:

@page "/call-dotnet-5"
@inject IJSRuntime JS

<PageTitle>Call .NET 5</PageTitle>

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

메모리 누수 및 가비지 수집을 허용하기 위해 개체 참조가 using var 구문으로 범위를 벗어날 때, DotNetObjectReference에서 만든 .NET 개체 참조가 삭제됩니다.

구성 요소에서 표시하는 출력은 Hello, Amy Pond! 필드에 이름이 Amy Pond 제공될 name 때입니다.

위의 구성 요소에서 .NET 개체 참조는 삭제됩니다. 클래스 또는 구성 요소가 DotNetObjectReference를 삭제하지 않으면 전달된 DotNetObjectReference에서 dispose를 호출하여 클라이언트에서 삭제합니다.

window.{JS FUNCTION NAME} = (dotNetHelper) => {
  dotNetHelper.invokeMethodAsync('{.NET METHOD ID}');
  dotNetHelper.dispose();
}

앞의 예에서:

  • {JS FUNCTION NAME} 자리 표시자는 JS 함수의 이름입니다.
  • 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.
  • {.NET METHOD ID} 자리 표시자는 .NET 메서드 식별자입니다.

구성 요소 인스턴스 .NET 메서드 도우미 클래스

도우미 클래스는 .NET 인스턴스 메서드를 Action으로 호출할 수 있습니다. 도우미 클래스는 다음과 같은 시나리오에서 유용합니다.

  • 동일한 형식의 여러 구성 요소가 동일한 페이지에 렌더링되는 경우
  • 여러 사용자가 동시에 동일한 구성 요소를 사용하는 서버 쪽 앱에서.

다음 예제에서

  • 구성 요소에는 앱 폴더의 Shared 공유 구성 요소인 여러 ListItem1 구성 요소가 포함되어 있습니다.
  • ListItem1 구성 요소는 메시지와 단추로 구성됩니다.
  • ListItem1 구성 요소 단추를 선택하면 해당 ListItem1UpdateMessage 메서드가 목록 항목 텍스트를 변경하고 단추를 숨깁니다.

다음 MessageUpdateInvokeHelper 클래스는 클래스를 인스턴스화할 때 지정된 Action을 호출하기 위해 JS 호출 가능 .NET 메서드인 UpdateMessageCaller를 유지 관리합니다.

MessageUpdateInvokeHelper.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class MessageUpdateInvokeHelper(Action action)
{
    private readonly Action action = action;

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}

다음 updateMessageCallerJS 함수는 UpdateMessageCaller .NET 메서드를 호출합니다.

<script>
  window.updateMessageCaller = (dotNetHelper) => {
    dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
    dotNetHelper.dispose();
  }
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

앞의 예제에서 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

다음 ListItem1 구성 요소는 부모 구성 요소에서 여러 번 사용할 수 있는 공유 구성 요소로, HTML 목록(<ul>...</ul> 또는 <ol>...</ol>)에 대해 목록 항목(<li>...</li>)을 만듭니다. 각 ListItem1 구성 요소 인스턴스는 Action이 해당 UpdateMessage 메서드로 설정된 MessageUpdateInvokeHelper의 인스턴스를 설정합니다.

ListItem1 구성 요소의 InteropCall 단추를 선택하면 MessageUpdateInvokeHelper 인스턴스에 대해 생성된 DotNetObjectReference를 사용하여 updateMessageCaller가 호출됩니다. 이렇게 하면 프레임워크가 해당 ListItem1MessageUpdateInvokeHelper 인스턴스에서 UpdateMessageCaller를 호출할 수 있습니다. 전달된 DotNetObjectReference는 JS(dotNetHelper.dispose())에서 삭제됩니다.

ListItem1.razor:

@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        if (messageUpdateInvokeHelper is not null)
        {
            await JS.InvokeVoidAsync("updateMessageCaller",
                DotNetObjectReference.Create(messageUpdateInvokeHelper));
        }
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        if (messageUpdateInvokeHelper is not null)
        {
            await JS.InvokeVoidAsync("updateMessageCaller",
                DotNetObjectReference.Create(messageUpdateInvokeHelper));
        }
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        if (messageUpdateInvokeHelper is not null)
        {
            await JS.InvokeVoidAsync("updateMessageCaller",
                DotNetObjectReference.Create(messageUpdateInvokeHelper));
        }
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        await JS.InvokeVoidAsync("updateMessageCaller",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        await JS.InvokeVoidAsync("updateMessageCaller",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}

messageUpdateMessage에 설정된 경우 UI를 업데이트하기 위해 StateHasChanged가 호출됩니다. StateHasChanged가 호출되지 않은 경우에는 Action이 호출될 때 UI가 업데이트되어야 한다는 것을 Blazor에서 알 수 없습니다.

다음 부모 구성 요소에는 각각 구성 요소의 인스턴스인 4개의 목록 항목이 ListItem1 포함됩니다.

CallDotnet6.razor:

@page "/call-dotnet-6"

<PageTitle>Call .NET 6</PageTitle>

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

다음 이미지는 두 번째 InteropCall 단추를 선택한 후 렌더링된 부모 구성 요소를 보여 줍니다.

  • 두 번째 ListItem1 구성 요소는 UpdateMessage Called! 메시지를 표시했습니다.
  • InteropCall 단추의 CSS display 속성이 none으로 설정되어 있기 때문에 두 번째 ListItem1 구성 요소에 대한 단추는 표시되지 않습니다.

Rendered 'CallDotNetExample6' component example

요소 속성에 할당된 DotNetObjectReference에서 호출한 구성 요소 인스턴스 .NET 메서드

DotNetObjectReference를 HTML 요소의 속성에 할당하면 구성 요소 인스턴스에서 .NET 메서드를 호출할 수 있습니다.

구성 요소 인스턴스 .NET 메서드 도우미 클래스 섹션에서 설명하는 것처럼, 이 방법은 다음 시나리오에서 유용합니다.

  • 동일한 형식의 여러 구성 요소가 동일한 페이지에 렌더링되는 경우
  • 여러 사용자가 동시에 동일한 구성 요소를 사용하는 서버 쪽 앱에서.
  • .NET 메서드는 JS 이벤트(예: onclick)가 아닌 Blazor 이벤트(예: @onclick)에서 호출됩니다.

다음 예제에서

  • 구성 요소에는 앱 폴더의 Shared 공유 구성 요소인 여러 ListItem2 구성 요소가 포함되어 있습니다.
  • ListItem2 구성 요소는 <span> 목록 항목 메시지와 display CSS 속성이 inline-block으로 설정된 두 번째 <span>로 구성됩니다.
  • ListItem2 구성 요소 목록 항목이 선택된 경우, ListItem2UpdateMessage 메서드는 display 속성을 none으로 설정하여 첫 번째 <span>의 목록 항목 텍스트를 변경하고 두 번째 <span>를 숨깁니다.

다음 assignDotNetHelperJS 함수는 dotNetHelper라는 속성의 요소에 DotNetObjectReference를 할당합니다.

<script>
  window.assignDotNetHelper = (element, dotNetHelper) => {
    element.dotNetHelper = dotNetHelper;
  }
</script>

다음 interopCallJS 함수는 전달된 요소에 대해 DotNetObjectReference를 사용하여 UpdateMessage라는 .NET 메서드를 호출합니다.

<script>
  window.interopCall = async (element) => {
    await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
  }
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

앞의 예제에서 변수 이름 dotNetHelper는 임의이며 원하는 이름으로 변경할 수 있습니다.

다음 ListItem2 구성 요소는 부모 구성 요소에서 여러 번 사용할 수 있는 공유 구성 요소로, HTML 목록(<ul>...</ul> 또는 <ol>...</ol>)에 대해 목록 항목(<li>...</li>)을 만듭니다.

ListItem2 구성 요소 인스턴스는 요소 참조(목록 항목의 첫 번째 <span> 요소)와 구성 요소 인스턴스를 사용하여 OnAfterRenderAsync에서 assignDotNetHelperJS 함수를 DotNetObjectReference로 호출합니다.

ListItem2 구성 요소의 메시지인 <span>가 선택된 경우, interopCall이 호출되어 <span> 요소를 매개 변수(this)로서 전달하며 이 매개 변수가 UpdateMessage .NET 메서드를 호출합니다. UpdateMessage에서는 message가 설정되고 두 번째 <span>display 속성이 업데이트된 경우 StateHasChanged가 호출되어 UI를 업데이트합니다. StateHasChanged가 호출되지 않으면 메서드가 호출될 때 UI가 업데이트되어야 한다는 것을 Blazor에서 알 수 없습니다.

구성 요소가 삭제되면 DotNetObjectReference가 삭제됩니다.

ListItem2.razor:

@inject IJSRuntime JS

<li>
    <span style="font-weight:bold;color:@color" @ref="elementRef" 
        onclick="interopCall(this)">
        @message
    </span>
    <span style="display:@display">
        Not Updated Yet!
    </span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";
    private string color = "initial";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        color = "MediumSeaGreen";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2> objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2> objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}

다음 부모 구성 요소에는 각각 구성 요소의 인스턴스인 4개의 목록 항목이 ListItem2 포함됩니다.

CallDotnet7.razor:

@page "/call-dotnet-7"

<PageTitle>Call .NET 7</PageTitle>

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

클라이언트 쪽 구성 요소의 동기 JS interop

이 섹션은 클라이언트 쪽 구성 요소에만 적용됩니다.

JS interop 호출은 호출된 코드가 동기인지 비동기인지와 관계없이 기본적으로 비동기적입니다. 기본적으로 호출은 비동기적이므로 구성 요소가 서버 쪽 및 클라이언트 쪽 렌더링 모드에서 호환되도록 합니다. 서버에서 모든 JS interop 호출은 네트워크 연결을 통해 전송되므로 비동기적이어야 합니다.

구성 요소가 WebAssembly에서만 실행된다는 것을 확실히 알고 있는 경우 동기 JS interop 호출을 하도록 선택할 수 있습니다. 이렇게 하면 비동기 호출을 수행하는 것보다 오버헤드가 약간 감소하며 결과를 기다리는 동안 중간 상태가 없기 때문에 렌더링 주기가 감소할 수 있습니다.

클라이언트 쪽 구성 요소에서 JavaScript에서 .NET으로 동기 호출을 하려면 대신 사용합니다 DotNet.invokeMethodDotNet.invokeMethodAsync.

동기 호출이 작동하는 경우는 다음과 같습니다.

  • 구성 요소는 WebAssembly에서 실행하기 위해 렌더링됩니다.
  • 호출된 함수는 값을 동기적으로 반환합니다. 함수는 async 메서드가 아니며 .NET Task 또는 JavaScript Promise를 반환하지 않습니다.

JavaScript 위치

JS interop 개요 문서에 설명된 접근 방식을 사용하여 JavaScript(JS) 코드를 로드합니다.

모듈을 사용하여 JS 로드 JS 하는 방법은 JavaScript 모듈 섹션의 JavaScript 격리에 있는 이 문서에 설명되어 있습니다 .

Warning

태그를 <script> 동적으로 업데이트할 수 없으므로 구성 요소가 정적 서버 쪽 렌더링(정적 SSR)을 채택하도록 보장되는 경우에만 구성 요소 파일.razor()에 태그를 <script> 배치합니다.

Warning

<script> 태그를 동적으로 업데이트할 수 없으므로 구성 요소 파일(.razor)에 <script> 태그를 넣지 마세요.

JavaScript 모듈에서의 JavaScript 격리

Blazor에서는 표준 JavaScript 모듈(ECMAScript 사양)에서 JavaScript(JS) 격리를 사용하도록 설정합니다. JavaScript 모듈 로드는 다른 유형의 웹앱과 Blazor 동일한 방식으로 작동하며, 앱에서 모듈을 정의하는 방법을 자유롭게 사용자 지정할 수 있습니다. JavaScript 모듈을 사용하는 방법에 대한 가이드는 MDN 웹 문서: JavaScript 모듈을 참조 하세요.

JS 격리는 다음과 같은 이점을 제공합니다.

  • 가져온 JS는 전역 네임스페이스를 더 이상 오염시키지 않습니다.
  • 라이브러리 및 구성 요소의 소비자는 관련 JS를 가져올 필요가 없습니다.

자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.

연산자를 사용하여 import() 동적 가져오기는 ASP.NET Core 및 Blazor다음에서 지원됩니다.

if ({CONDITION}) import("/additionalModule.js");

앞의 예제 {CONDITION} 에서 자리 표시자는 모듈을 로드해야 하는지 여부를 결정하는 조건부 검사 나타냅니다.

브라우저 호환성을 보려면 다음을 사용할 수 있습니다. JavaScript 모듈: 동적 가져오기를 참조하세요.

순환 개체 참조 방지

순환 참조를 포함하는 개체는 다음 중 하나에 대해 클라이언트에서 직렬화될 수 없습니다.

  • .NET 메서드 호출
  • 반환 형식에 순환 참조가 있는 경우 C#에서 JavaScript 메서드 호출

바이트 배열 지원

Blazor는 최적화된 바이트 배열 JS(JavaScript) interop을 지원하여 바이트 배열이 Base64로 인코딩/디코딩되는 것을 방지합니다. 다음 예제에서는 JS interop을 사용하여 바이트 배열을 .NET에 전달합니다.

sendByteArrayJS 함수를 제공합니다. 함수는 구성 요소의 단추에 의해 호출에 invokeMethodAsync 어셈블리 이름 매개 변수를 포함하고 값을 반환하지 않는 정적으로 호출됩니다.

<script>
  window.sendByteArray = () => {
    const data = new Uint8Array([0x45,0x76,0x65,0x72,0x79,0x74,0x68,0x69,
      0x6e,0x67,0x27,0x73,0x20,0x73,0x68,0x69,0x6e,0x79,0x2c,
      0x20,0x43,0x61,0x70,0x74,0x61,0x69,0x6e,0x2e,0x20,0x4e,
      0x6f,0x74,0x20,0x74,0x6f,0x20,0x66,0x72,0x65,0x74,0x2e]);
    DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
      .then(str => {
        alert(str);
      });
  };
</script>

참고 항목

JS 위치에 대한 일반적인 지침과 및 프로덕션 앱에 대한 권장 사항은 ASP.NET Core Blazor JavaScript 상호 운용성(JSinterop)을 참조하세요.

CallDotnet8.razor:

@page "/call-dotnet-8"
@using System.Text

<PageTitle>Call .NET 8</PageTitle>

<h1>Call .NET Example 8</h1>

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

<p>
    Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

CallDotNetExample8.razor:

@page "/call-dotnet-example-8"
@using System.Text

<PageTitle>Call .NET 8</PageTitle>

<h1>Call .NET Example 8</h1>

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

<p>
    Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

CallDotNetExample8.razor:

@page "/call-dotnet-example-8"
@using System.Text

<PageTitle>Call .NET 8</PageTitle>

<h1>Call .NET Example 8</h1>

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

<p>
    Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

.NET에서 JavaScript를 호출할 때 바이트 배열을 사용하는 방법에 대한 자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.

JavaScript에서 .NET로 스트리밍

Blazor는 JavaScript에서 .NET으로 직접 데이터를 스트리밍하는 것을 지원합니다. Microsoft.JSInterop.IJSStreamReference 인터페이스를 사용하여 스트림을 요청합니다.

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsyncStream을 반환하고 다음 매개 변수를 사용합니다.

  • maxAllowedSize: JavaScript에서 읽기 작업에 허용된 최대 바이트 수이며, 지정하지 않으면 기본적으로 512,000바이트로 설정됩니다.
  • cancellationToken: 읽기를 취소하기 위한 CancellationToken입니다.

JavaScript에서:

function streamToDotNet() {
  return new Uint8Array(10000000);
}

C# 코드에서:

var dataReference = 
    await JS.InvokeAsync<IJSStreamReference>("streamToDotNet");
using var dataReferenceStream = 
    await dataReference.OpenReadStreamAsync(maxAllowedSize: 10_000_000);

var outputPath = Path.Combine(Path.GetTempPath(), "file.txt");
using var outputFileStream = File.OpenWrite(outputPath);
await dataReferenceStream.CopyToAsync(outputFileStream);

앞의 예에서:

  • JS는 삽입된 IJSRuntime 인스턴스입니다. IJSRuntime은 Blazor 프레임워크에 의해 등록됩니다.
  • dataReferenceStream은 현재 사용자의 임시 폴더 경로(GetTempPath)에서 디스크(file.txt)에 기록됩니다.

ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출에서는 DotNetStreamReference를 사용하여 .NET에서 JavaScript로 스트리밍하는 역방향 작업을 설명합니다.

ASP.NET Core Blazor 파일 업로드에서는 Blazor에서 파일을 업로드하는 방법을 설명합니다. 서버 쪽 구성 요소에서 데이터를 스트리밍 <textarea> 하는 양식 예제는 ASP.NET Core Blazor 양식 문제 해결을 참조하세요.

JavaScript [JSImport]/[JSExport] interop

이 섹션은 클라이언트 쪽 구성 요소에 적용됩니다.

인터페이스를 기반으로 IJSRuntime 하는 'interop 메커니즘을 JS 사용하여 Blazor클라이언트 쪽 구성 요소에서 JavaScript(JS)와 상호 작용하는 대신.JS[JSImport]/[JSExport]NET 7 이상을 대상으로 하는 앱에서 interop API를 사용할 수 있습니다.

자세한 내용은 JavaScript JSImport/JSExport interop 및 ASP.NET Core Blazor를 참조하세요.

JavaScript interop 개체 참조 삭제

JavaScript(JS) interop 문서의 예제는 일반적인 개체 삭제 패턴을 보여 줍니다.

JS interop 개체 참조는 참조를 만드는 interop 호출의 측면에 JS 있는 식별자에 의해 키가 지정된 맵으로 구현됩니다. .NET 또는 JS 쪽 Blazor 에서 개체 삭제가 시작되면 맵에서 항목을 제거하고 개체에 대한 다른 강력한 참조가 없는 한 개체를 가비지 수집할 수 있습니다.

최소한 .NET 관리 메모리 누수 방지를 위해 항상 .NET 쪽에서 만든 개체를 삭제합니다.

구성 요소 삭제 중 DOM 클린up 작업

자세한 내용은 ASP.NET Core Blazor JavaScript 상호 운용성(JS)을 참조하세요.

회로가 없는 JavaScript interop 호출

자세한 내용은 ASP.NET Core Blazor JavaScript 상호 운용성(JS)을 참조하세요.

추가 리소스