Blazor JavaScript 互操作性

关于 JavaScript 位置

在 标记中加载脚本

<head>
    <script src="js/first.js"></script>
    <script>
      window.jsMethod = (methodParameter) => {
        ...
      };
    </script>
</head>

在 标记中加载脚本

<body>
    <script src="js/first.js"></script>
    <script>
      window.jsMethod = (methodParameter) => {
        ...
      };
    </script>
</body>

组件并置

组件:CountComponent.razor
组件:CountComponent.razor.cs
样式:CountComponent.razor.css
脚本:CountComponent.razor.js

从.NET 调用 JS

在 wwwroot 下创建 app.js 文件

function helloWorld() {
    alert('Hello World');
}

function add(a, b) {
    return a + b;
}

在 App.razor 中引入 JS 文件

当前 Web 项目脚本引入

<head>
    <script src="@Assets["app.js"]"></script>
</head>

如果使用了 RCL 组件库

<head>
    <script src="_content/LibraryName/app.js"></script>
</head>

在 .NET 组件中调用 JS 方法

@inject IJSRuntime JS

<button class="btn btn-primary" @onclick="SayHello">SayHello</button>
<button class="btn btn-primary" @onclick="Add">Add</button>
<p>Current count: @result</p>

@code {

    private int result = 0;

    private async Task SayHello()
    {
        await JS.InvokeVoidAsync("helloWorld");
    }

    private async Task Add()
    {
        result = await JS.InvokeAsync<int>("add", result, 1);
        StateHasChanged();
    }
}

基于模块化的 JS 文件

export function helloWorld() {
    alert('Hello World');
}

export function add(a, b) {
    return a + b;
}

在 MyComponent.razor 组件中写如下代码

@inject IJSRuntime JS
@implements IAsyncDisposable

@code {
    private IJSInProcessObjectReference? module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var jsInProcess = (IJSInProcessRuntime)JS;
            module = await JS.InvokeAsync<IJSInProcessObjectReference>("import", "./path/to/MyComponent.razor.js");
            var value = module.Invoke<string>("javascriptFunctionIdentifier");
        }
    }

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

<div @ref="divElement" style="margin-top:2000px">
    Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
    private ElementReference divElement;
    private int scrollPosition;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var jsInProcess = (IJSInProcessRuntime)JS;
            var module = await jsInProcess.InvokeAsync<IJSInProcessObjectReference>("import", "./path/to/MyComponent.razor.js");
            scrollPosition = await module.InvokeAsync<int>("getScrollPosition", divElement);
        }
    }
}

从 JS 调用 .NET 方法

提供两个静态方法

DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethod('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});    

在 C## 组件中提供一个静态方法

@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

可以指定参数修改方法名

[JSInvokable("DifferentMethodName")]
public static Task{<T>} {.NET METHOD ID}()
{
    ...
}

通过 dotNetHelper 调用 .NET 方法

通过将实例包装在 DotNetObjectReference 中并对其调用 Create,将 .NET 实例通过引用传递给 JS。

@inject IJSRuntime JS
@implements IAsyncDisposable
@code {
    private DotNetObjectReference<MyComponent>? dotNetHelper;

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

    public async ValueTask DisposeAsync()
    {
        if (dotNetHelper is not null)
        {
            await dotNetHelper.DisposeAsync();
        }
    }
}