Blazor學習之旅(12)JavaScript與Blazor的互操作

大家好,我是Edison。

上一篇我們學習了Blazor+SignalR開發簡單的實時應用程序,這一篇我們瞭解下Blazor和JavaScript的互操作性。

有了Blazor還需要JavaScript?

我們都知道,在Blazor中我們可以輕鬆地使用C#組件而不是JavaScript來創建Web應用程序,但是,這並不代表我們不能繼續使用JavaScript提供的便利。很

多時候,我們可能希望繼續使用JavaScript提供的函數來實現某些功能,這時,我們可以用Blazor和JavaScript的互操作性(也稱爲JS互操作)來調用Blazor應用中的JavaScript庫,並從C#代碼直接調用JavaScript函數。當然,也可以使用JS互操作性從JavaScript函數調用C#方法。

這種場景經常發生在:有時候需要使用現有的JavaScript庫,例如一些開源JavaScript庫以專門的方式呈現組件和處理用戶界面元素,又或者你可能擁有一些開源JavaScript庫的開發調試經驗,是個JavaScript庫的老鳥了,你希望重用該JavaScript代碼而不是將其轉換爲C#。那麼,這個時候,你可能就需要用上JS互操作性了。

接下來,我們就來看看如何在Blazor應用中加載JavaScript代碼,又如何在JavaScript中調用.NET代碼。

在Blazor中調用JavaScript代碼

加載方式

將JavaScript添加到Blazor應用的方式與添加到標準HTML Web應用一樣,都是使用HTML的<script>元素。我們可以在 Pages/_Layout.cshtml 文件或 wwwroot/index.html文件中的現有 <script src="_framework/blazor.*.js"></script> 標記後添加 <script> 標記即可。

NOTE:最好不要將JavaScript腳本放在頁面的<head>元素中。

將JavaScript庫或腳本添加之後,我們就可以在C#代碼中通過使用 IJSRuntime 接口調用JavaScript函數了。不過,你需要提前將 IJSRuntime 實例注入Blazor頁面中。

IJSRuntime 接口用於調用JavaScript代碼的 InvokeAsync(有返回值) 和 InvokeVoidAsync(無返回值) 兩個方法。顧名思義,這兩個方法都是異步的,因此你需要在使用時標註await來獲取結果。

InvokeAsync 或 InvokeVoidAsync 方法的接收參數,第一個是要調用的JavaScript函數的名稱,比如 confirm 這個方法名。第二個則是這個函數所需的任何參數。

需要注意的是:

  • JavaScript函數必須屬於 window 作用域 或 window 子作用域;

  • 傳入的參數必須是可序列化爲JSON的;

DEMO

假設我們已經有了一個Blazor Server應用程序,你可以從這裏獲取Code:https://github.com/Coder-EdisonZhou/BlazorSamples

這裏我們改寫一下經典的Counter頁面,將原來的按鈕直接加一改爲調用JavaScript的confirm函數彈出一個確認框,確認後再加一。

爲了實現這個功能,我們需要改寫如下:

Step1. 注入IJSRuntime抽象實例

[Inject]
public IJSRuntime JavaScript { get; set; }

Step2. 改寫原來的button按鈕調用IncrementCountConfirmation方法

@* <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> *@
<button class="btn btn-primary" @onclick="@(async() => await IncrementCountConfirmation())">Click me</button>

Step3. 實現IncrementCountConfirmation方法

@code {
    private int currentCount = 0;

    [Parameter]
    public int IncrementAmount { get; set; } = 1;

    [Inject]
    public IJSRuntime JavaScript { get; set; }

    private void IncrementCount()
    {
        currentCount += IncrementAmount;
    }
    
    private async Task IncrementCountConfirmation()
    {
        if (await JavaScript.InvokeAsync<bool>(
          "confirm", 
          "Do you want to increment the count?"))
        {
            currentCount += IncrementAmount;
        }
    }
}

最終的效果如下圖:

那麼,如果想要使用第三方JavaScript庫的函數該如何做呢?

你只需要在 Pages/_Layout.cshtml 文件的末尾,在現有 <script src="_framework/blazor.*.js"></script> 標記後添加你需要引入的JavaScript庫即可。
然後,你就可以在C#代碼中繼續通過 IJSRuntime 調用第三方JavaScript庫中的函數了。

在JavaScript中調用C#代碼

加載方式

在JavaScript中若想調用C#代碼可以使用 DotNet實用工具類(JS互操作的一部分)來運行Blazor代碼中定義的.NET方法。在這個工具類中提供了 invokeMethod 和 invokeMethodAsync 兩個函數,顧名思義,一個是同步的,另一個是異步的。

invokeMethodAsync方法返回 JavaScript Promise。

需要注意的是:

  • 要調用的.NET方法需要使用 JSInvokableAttribute 標記

  • 且該方法必須是 public的

  • 且該方法任何參數都必須可序列化爲JSON

DEMO

這裏我們還是改寫一下剛剛的Counter頁面,增加一個button用於在JavaScript中調用.NET靜態方法。

Step1. 添加HTML與JavaScript

<h1>Call .NET Example From JavaScript</h1>

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

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

Step2. 添加.NET方法並標註 JSInvokable

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

效果演示:

那麼,如果是.NET實例方法,該如何調用呢?這個就稍微複雜一丟丟了。

還是來個Demo吧:

Step1. 添加HTML和JavaScript示例:

<h1>Call .NET Example From JavaScript - Sample 2</h1>

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

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

<p>
    @result
</p>

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

Step2. 添加.NET方法並標註 JSInvokable,還需要聲明一個 DotNetObjectReference對象便於進行資源釋放,以免引起內存泄露的問題;

@code {    
    [Inject]
    public IJSRuntime JavaScript { get; set; }

    private string? name;
    private string? result;
    private DotNetObjectReference<Counter>? objRef;

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

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

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

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

效果演示:

小結

本篇,我們瞭解了什麼是Blazor中的JS互操作,並通過兩個DEMO瞭解瞭如何在Blazor中加載JavaScript代碼 以及 如何在JavaScript中調用.NET代碼,相信對你會有所幫助。

參考代碼

GitHub:https://github.com/EdisonChou/BlazorSamples/tree/main

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章