ASP.NET Core Blazor WebAssembly 之 .NET JavaScript互调
Blazor WebAssembly可以在浏览器上跑C#代码,但是很多时候显然还是需要跟JavaScript打交道。比如操作dom,当然跟angular、vue一样不提倡直接操作dom;比如浏览器的后退导航。反之JavaScript也有可能需要调用C#代码来实现一些功能,毕竟客户的需求是千变万化的,有的时候只能通过一些hack的手段来实现。
.NET调用JavaScript函数
使用JSRuntime.InvokeVoidAsync调用无返回值的JavaScript函数
显然我们的.NET类库里不会有JavaScript内置的alert方法来显示提示,这里演示下如何调用JavaScript的alert方法:
<h3>.net call javascript</h3><button @onclick="CallJs"> Call alert</button>@inject IJSRuntime jsRuntime@code { private void CallJs() { jsRuntime.InvokeVoidAsync("alert", "this message from .net runtime ."); }}
使用JSRuntime.InvokeVoidAsync调用具有返回值的JavaScript函数
我们在JavaScript环境定义一个加法函数然后.NET这边调用拿到结果:
<script> function add(a, b) { return a + b; } </script>
注意:JavaScript代码要放到wwwroot/index.html页面上里,不能直接放在组件里。
组件代码:
<h3>.net call javascript</h3>sum: @sum<button @onclick="CallJs"> Call Add</button>@inject IJSRuntime jsRuntime@code { private int sum = 0; private async void CallJs() { sum = await jsRuntime.InvokeAsync<int>("add", sum, 2); this.StateHasChanged(); }}
运行一下:
JavaScript调用.NET方法
JavaScript调用.NET静态方法
JavaScript调用.NET静态方法比较简单,把静态方法加上[JSInvokable],然后在JavaScript环境使用DotNet对象直接call就行:
定义.NET静态方法:
[JSInvokable] public static string GetNow() { return DateTime.Now.ToString(); }
使用JavaScript调用GetNow:
$(document).ready( setTimeout(() => { $('#btn1').on('click', function () { DotNet.invokeMethodAsync('BlazorWasmComponent', 'GetNow') .then(data => { alert(data); }); }) }, 10000) );
由于Blazor渲染UI结束后按钮才会插入到dom树上,所以这里使用一个傻办法让绑定事件的JavaScript代码置后运行。
运行一下:
JavaScript调用组件里的方法
JavaScript调用组件里的方法比较绕,其实还是通过一个静态方法作为入口,把实例方法绑定一个静态delegate,然后让这个静态方法去执行delegate。
.NET代码:
<h3>javascript call .net</h3><button id="btn1"> Js call .net</button>@inject IJSRuntime jsRuntime@code { [JSInvokable] public static string GetNow() { return Act(""); } public static Func<string, string> Act; protected override void OnInitialized() { Act = GetNowInInstance; base.OnInitialized(); } public string GetNowInInstance(string str) { return DateTime.Now.ToString(); }}
JavaScript代码:
$(document).ready( setTimeout(() => { $('#btn1').on('click', function () { DotNet.invokeMethodAsync('BlazorWasmComponent', 'GetNow') .then(data => { alert(data); }); }) }, 10000) );
运行一下:
调用对象的方法
Blazor还可以把.NET对象(引用)直接传递到JavaScript运行时来让JavaScript直接调用.NET对象的方法。
总的来说大概分4步:
实例化.net对象
DotNetObjectReference.Create方法把.NET对象包装
通过JSRuntime调用一个JavaScript方法把第二步生成的对象传递到JavaScript运行时
在JavaScript侧通过invokeMethodAsync方法调用.NET对象里的方法
下面演示下把组件整个实例传递出去,然后调用里面的GetNowInInstance方法。
.net代码:
<h3>javascript call .net</h3><button id="btn1"> Js call .net</button>@implements IDisposable@inject IJSRuntime jsRuntime@code { IDisposable _objRef; protected async override Task OnInitializedAsync() { _objRef = DotNetObjectReference.Create(this); await jsRuntime.InvokeAsync<string>( "receiveNetObj", _objRef); base.OnInitialized(); } [JSInvokable] public string GetNowInInstance() { return DateTime.Now.ToString(); } public void Dispose() { _objRef?.Dispose(); }}
注意:把.NET对象传递到JavaScript运行时存在内存泄漏的风险,所以组件需要实现IDisposable接口,在Dispose方法内调用objRef的Dispose方法来释放内存。
JavaScript代码:
var _netObj = null; function receiveNetObj(obj) { _netObj = obj; } $(document).ready( setTimeout(() => { $('#btn1').on('click', function () { _netObj.invokeMethodAsync("GetNowInInstance").then( r => alert(r) ); }) }, 10000) );
运行一下:
总结
使用JSRuntime可以在.NET里调用JavaScript的方法,这些方法必须是全局的,也就是挂载在window对象上的。
在JavaScript里调用.NET方法主要有两种:
通过DotNet方式调用.NET的静态方法
把.NET对象直接传递到JavaScript运行时来调用对象上的方法