1.環境
VS2019 16.5.1
.NET Core SDK 3.1.200
Blazor WebAssembly Templates 3.2.0-preview2.20160.5
2.前言
Blazor的存在可以讓我們再前端以高性能運行代碼,但是有些時候我們不得不需要使用JS來進行一些操作,尤其是在使用第三方JS庫的時候,而在JS執行完畢后,可能還需要JS通知C#執行的結果,這時候就需要使用C#調用JS或者是JS調用C#。
3.C#調用JS
3.1.函數定義
C#調用JS是通過IJSRuntime來實現的。IJSRuntime定義了兩個方法(當然,這兩個方法都有重載,但是這兩個方法是常見的):
ValueTask<TValue> InvokeAsync<TValue>(string identifier, object[] args); ValueTask InvokeVoidAsync<TValue>(string identifier, object[] args);
InvokeAsync和InvokeVoidAsync的定義類似,形參identifier代表要執行的js函數名(要求必須掛載在window對象上),可以為JSON格式的標識,args則是js函數所需要的參數。其中,InvokeAsync用於執行有返回值的js函數,InvokeVoidAsync用於執行無返回值的js函數,這是兩者的區別。
我們可以看到,IJSRuntime中的這兩個函數都是異步函數,這是因為如果Blazor是服務端渲染的格式,其使用SignalR進行交互,JS互操作調用都必須是異步的。如果我們希望在WebAssembly應用程序(客戶端渲染)下同步調用js函數,則可以將IJSRuntime對象轉換為IJSInProcessRuntime對象,IJSInProcessRuntime對象中定義了一個同步方法Invoke,其形參與上述的兩個相同。
T Invoke<T>(string identifier, params object[] args)
注意:IJSRuntime在Server與Client渲染模式中都可以使用,IJSInProcessRuntime只可在Client渲染模式中使用。
3.2.IJSRuntime的注入
如果我們直接在razor組件中使用IJSRuntime對象,則可以通過@inject指令將IJSRuntime對象注入到組件中;如果我們需要在類中調用IJSRuntime對象,則需要使用Inject屬性注解進行注入:
//組件中注入 @inject IJSRuntime JsRuntime //類中注入 [Inject] public IJSRuntime JsRuntime { get; set; }
3.3.使用
新建一個Blazor WebAssembly應用程序,打開Index.razor頁面,在路由屬性下插入以下代碼:
@inject IJSRuntime JsRuntime <div class="jumbotron bg-white border border-primary"> <h5>C#與JS互操作</h5> <div> <button @onclick="OnClick" class="btn btn-primary">交互</button> </div> </div>
在這里定義了一個button,並為它添加了onclick事件,注意我們在為HTML的原生事件添加綁定時,需要在事件前添加“@”符號。接下來,我們實現OnClick事件響應函數。OnClick函數的返回結果可以是Task,也可以為void,此外,還可以有一個事件參數(像js中定義事件函數一樣,可以不用寫e)。OnClick的定義如下:
private async Task OnClick(MouseEventArgs e) { Console.WriteLine("OnClick is executing"); var name = "world"; var a = 11; var b = 22; var jsRunResult = await JsRuntime.InvokeAsync<string>("interop.runJs", name, a, b); Console.WriteLine($"interop.runJs return:{jsRunResult}"); }
在OnClick中使用JsRuntime調用了一個js函數runJs。runJs可以通過window.interop.runJs被調用。interop.runJs的定義如下(為了區分C# Console.WriteLine的輸出,這里使用console.warn,使兩者具備不同的背景色):
window.interop = { runJs: (name, a, b) => { console.warn("runJs is executing"); console.warn("hello " + name); return "OK " + (a + b); } }
點擊“交互”按鈕,瀏覽器中控制台的輸出結果如下(WASM: 是C#Console.WriteLine默認輸出的前綴):
4.JS調用C#
從JS調用C#有兩種方式,一種是調用靜態方法,另外一種是調用實例方法,無論那種方式,C#中能被JS調用的函數都需要標注JSInvokable屬性注解。
4.1.靜態方法的調用
4.1.1.介紹
靜態方法的調用是通過DotNet.invokeMethod或DotNet.invokeMethodAsync來實現的:
DotNet.invokeMethod("程序集的名稱", "靜態方法名稱",參數1,…,參數n);
DotNet.invokeMethodAsync定義與DotNet.invokeMethod定義相似,兩者區別在於DotNet.invokeMethodAsync的返回值為Promise,DotNet.invokeMethod結果就是函數的返回值。
4.1.2.實例
首先,我們在Index頁面中定義一個靜態函數Sum:
[JSInvokable] public static Task<int> Sum(int a, int b) { Console.WriteLine("Sum is executing"); return Task.FromResult(a + b); }
Sum的作用就是將用於替換runJs中放入求和。
然后我們添加一個runCsharp JS函數,並修改runJs的返回值為”return "OK " + runCsharp(a, b);“。runCsharp的定義如下:
const runCsharp = (a, b) => { console.warn("runCsharp is executing"); //invokeResult是Task序列化的結果 let invokeResult = DotNet.invokeMethod("BlazorInterop", "Sum", a, b); if (invokeResult.isCompletedSuccessfully) { return invokeResult.result; } return -1; };
點擊“交互”按鈕,瀏覽器中控制台的輸出結果如下:
注意:由於Sum的返回值為Task,因此其返回結果invokeResult是Task的序列化結果,通過DotNet.invokeMethodAsync(…).then(result=>)得到的result則是求和的結果。
4.2.實例方法的調用
4.2.1. 介紹
實例方法顧名思義,是指利用組件的實例調用方法,因此需要先將要執行的函數所在類的實例(可以不是組件)傳遞到JS中。這個實例並不是單純的new的結果,而是使用DotNetObjectReference對new進行封裝的結果:
//組件實例封裝: DotNetObjectReference.Create(this) //類實例封裝: var classInstance = new SomClass(); DotNetObjectReference.Create(classInstance)
4.2.2.實例
首先,在Index頁面中定義一個新的button,並為這個button綁定事件方法OnClick2:
<button @onclick="OnClick2" class="btn btn-primary">交互2</button>
OnClick2的定義如下:
private void OnClick2() { IJSInProcessRuntime SyncJsRuntime = JsRuntime as IJSInProcessRuntime; var a = 11; var b = 22; var jsRunResult = SyncJsRuntime.Invoke<string>("interop.runJs2",DotNetObjectReference.Create(this),a, b); Console.WriteLine($"interop.runJs2 return:{jsRunResult}"); }
OnClick2中使用IJSInProcessRuntime同步調用了一個JS函數runJs2:
runJs2: (objInstance, a, b) => { let invokeResult = objInstance.invokeMethod("Multiply", a, b); console.warn(invokeResult); return "OK:" + invokeResult; }
runJs2有三個參數:對象實例、a、b,然后通過對象實例調用了Index組件中的一個Multiply函數對a、b進行求積。Multiply函數定義如下:
[JSInvokable] public int Multiply(int a, int b) { Console.WriteLine("Multiply is executing"); return a * b; }
點擊“交互2”按鈕,瀏覽器中控制台的輸出結果如下:
代碼:https://github.com/zxyao145/LearningBlazor/tree/master/BlazorInterop
本文參考: