ASP.NET Core Blazor 核心功能二:Blazor与JavaScript互操作——让Web开发更灵活

news/2025/11/5 21:34:50/文章来源:https://www.cnblogs.com/shenchuanchao/p/19194651

嗨,大家好!我是码农刚子。今天我们来聊聊Blazor中C#与JavaScript互操作。我知道很多同学在听到"Blazor"和"JavaScript"要一起工作时会有点懵,但别担心,我会用最简单的方式带你掌握这个技能!

为什么要学JavaScript互操作?

想象一下:你正在用Blazor开发一个超棒的应用,但突然需要用到某个只有JavaScript才能实现的炫酷效果,或者要集成一个超好用的第三方JS库。这时候,JavaScript互操作就是你的救星!

简单来说,它让Blazor和JavaScript可以"握手合作",各展所长。下面我们就从最基础的部分开始。

1. IJSRuntime - 你的JavaScript通行证

在Blazor中,IJSRuntime是与JavaScript沟通的桥梁。获取它超级简单:

@inject IJSRuntime JSRuntime<button @onclick="ShowAlert">点我弹窗!</button>
@code {private async Task ShowAlert(){await JSRuntime.InvokeVoidAsync("alert", "Hello from Blazor!");}
}

image

就两行关键代码:

  • @inject IJSRuntime JSRuntime - 拿到通行证
  • InvokeVoidAsync - 调用不返回值的JS函数

实际场景:比如用户完成某个操作后,你想显示一个提示,用这种方式就特别方便。

2. 调用JavaScript函数 - 不只是简单弹窗

当然,我们不会只满足于弹窗。来看看更实用的例子:

首先,在wwwroot/index.html中添加我们的JavaScript工具函数:

<script>// 创建命名空间避免全局污染window.myJsHelpers = {showNotification: function (message, type) {// 模拟显示一个漂亮的提示框const notification = document.createElement('div');notification.style.cssText = `position: fixed;top: 20px;right: 20px;padding: 15px 20px;border-radius: 5px;color: white;z-index: 1000;transition: all 0.3s ease;`;if (type === 'success') {notification.style.backgroundColor = '#28a745';} else if (type === 'error') {notification.style.backgroundColor = '#dc3545';} else {notification.style.backgroundColor = '#17a2b8';}notification.textContent = message;document.body.appendChild(notification);// 3秒后自动消失setTimeout(() => {notification.remove();}, 3000);},getBrowserInfo: function () {return {userAgent: navigator.userAgent,language: navigator.language,platform: navigator.platform};},// 带参数的计算函数
        calculateDiscount: function (originalPrice, discountPercent) {return originalPrice * (1 - discountPercent / 100);}};
</script>

然后在Blazor组件中使用:

@inject IJSRuntime JSRuntime<div class="demo-container"><h3>JavaScript函数调用演示</h3><button @onclick="ShowSuccessNotification" class="btn btn-success">显示成功提示</button><button @onclick="ShowErrorNotification" class="btn btn-danger">显示错误提示</button><button @onclick="GetBrowserInfo" class="btn btn-info">获取浏览器信息</button><button @onclick="CalculatePrice" class="btn btn-warning">计算折扣价格</button>@if (!string.IsNullOrEmpty(browserInfo)){<div class="alert alert-info mt-3"><strong>浏览器信息:</strong> @browserInfo</div>}@if (discountResult > 0){<div class="alert alert-success mt-3"><strong>折扣价格:</strong> ¥@discountResult</div>}
</div>
@code {private string browserInfo = "";private decimal discountResult;private async Task ShowSuccessNotification(){await JSRuntime.InvokeVoidAsync("myJsHelpers.showNotification", "操作成功!数据已保存。", "success");}private async Task ShowErrorNotification(){await JSRuntime.InvokeVoidAsync("myJsHelpers.showNotification", "出错了!请检查网络连接。", "error");}private async Task GetBrowserInfo(){var info = await JSRuntime.InvokeAsync<BrowserInfo>("myJsHelpers.getBrowserInfo");browserInfo = $"语言: {info.Language}, 平台: {info.Platform}";}private async Task CalculatePrice(){discountResult = await JSRuntime.InvokeAsync<decimal>("myJsHelpers.calculateDiscount", 1000, 20); // 原价1000,8折
    }// 定义接收复杂对象的类private class BrowserInfo{public string UserAgent { get; set; }public string Language { get; set; }public string Platform { get; set; }}
}

image

image

Note

  • 使用InvokeVoidAsync调用不返回值的函数
  • 使用InvokeAsync<T>调用有返回值的函数,记得指定返回类型
  • 复杂对象会自动序列化/反序列化

3. 把.NET方法暴露给JavaScript - 双向操作

有时候,我们也需要让JavaScript能调用我们的C#方法。这就用到[JSInvokable]特性了。

@inject IJSRuntime JSRuntime
@implements IDisposable<div class="demo-container"><h3>.NET方法暴露演示</h3><div class="mb-3"><label>消息内容:</label><input @bind="message" class="form-control" /></div><div class="mb-3"><label>重复次数:</label><input type="number" @bind="repeatCount" class="form-control" /></div><button @onclick="RegisterDotNetMethods" class="btn btn-primary">注册.NET方法给JavaScript使用</button><div id="js-output" class="mt-3 p-3 border rounded"><!-- JavaScript会在这里输出内容 --></div>
</div>
@code {private string message = "Hello from .NET!";private int repeatCount = 3;private DotNetObjectReference<MyComponent> dotNetHelper;protected override void OnInitialized(){dotNetHelper = DotNetObjectReference.Create(this);}private async Task RegisterDotNetMethods(){await JSRuntime.InvokeVoidAsync("registerDotNetHelper", dotNetHelper);}[JSInvokable]public string GetFormattedMessage(){return string.Join(" ", Enumerable.Repeat(message, repeatCount));}[JSInvokable]public async Task<string> ProcessDataAsync(string input){// 模拟一些异步处理await Task.Delay(500);return $"处理后的数据: {input.ToUpper()} (处理时间: {DateTime.Now:HH:mm:ss})";}[JSInvokable]public void ShowAlert(string alertMessage){// 这个方法会被JavaScript调用// 在实际应用中,你可能会更新组件状态或触发其他操作Console.WriteLine($"收到JavaScript的警告: {alertMessage}");}public void Dispose(){dotNetHelper?.Dispose();}
}

对应的JavaScript代码:

// 在index.html中添加
function registerDotNetHelper(dotNetHelper) {// 存储.NET引用供后续使用window.dotNetHelper = dotNetHelper;// 演示调用.NET方法
    callDotNetMethods();
}async function callDotNetMethods() {if (!window.dotNetHelper) {console.error('.NET helper 未注册');return;}try {// 调用无参数的.NET方法const message = await window.dotNetHelper.invokeMethodAsync('GetFormattedMessage');// 调用带参数的异步.NET方法const processed = await window.dotNetHelper.invokeMethodAsync('ProcessDataAsync', 'hello world');// 调用void方法window.dotNetHelper.invokeMethodAsync('ShowAlert', '这是从JS发来的消息!');// 在页面上显示结果const output = document.getElementById('js-output');output.innerHTML = `<strong>来自.NET的消息:</strong> ${message}<br><strong>处理后的数据:</strong> ${processed}`;} catch (error) {console.error('调用.NET方法失败:', error);}
}

image

Note

  • 记得使用DotNetObjectReference来创建引用
  • 使用Dispose()及时清理资源
  • 异步方法要返回TaskTask<T>

4. 使用JavaScript库 - 集成第三方神器

这是最实用的部分!让我们看看如何集成流行的JavaScript库。

示例:集成Chart.js图表库

首先引入Chart.js:

<!-- 在index.html中 -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

创建图表辅助函数:

// 在index.html中或单独的JS文件
window.chartHelpers = {createChart: function (canvasId, config) {const ctx = document.getElementById(canvasId).getContext('2d');return new Chart(ctx, config);},updateChart: function (chart, data) {chart.data = data;chart.update();},destroyChart: function (chart) {chart.destroy();}
};

Blazor组件:

@inject IJSRuntime JSRuntime
@implements IDisposable<div class="chart-demo"><h3>销售数据图表</h3><canvas id="salesChart" width="400" height="200"></canvas><div class="mt-3"><button @onclick="LoadSalesData" class="btn btn-primary">加载销售数据</button><button @onclick="SwitchToProfitChart" class="btn btn-secondary">切换到利润图表</button></div>
</div>
@code {private IJSObjectReference chartInstance;private bool isSalesData = true;protected override async Task OnAfterRenderAsync(bool firstRender){if (firstRender){await InitializeChart();}}private async Task InitializeChart(){var config = new{type = "bar",data = new{labels = new[] { "一月", "二月", "三月", "四月", "五月" },datasets = new[]{new{label = "销售额",data = new[] { 65, 59, 80, 81, 56 },backgroundColor = "rgba(54, 162, 235, 0.5)",borderColor = "rgba(54, 162, 235, 1)",borderWidth = 1}}},options = new{responsive = true,plugins = new{title = new{display = true,text = "月度销售数据"}}}};chartInstance = await JSRuntime.InvokeAsync<IJSObjectReference>("chartHelpers.createChart", "salesChart", config);}private async Task LoadSalesData(){var newData = new{labels = new[] { "一月", "二月", "三月", "四月", "五月", "六月" },datasets = new[]{new{label = "销售额",data = new[] { 65, 59, 80, 81, 56, 75 },backgroundColor = "rgba(54, 162, 235, 0.5)"}}};await JSRuntime.InvokeVoidAsync("chartHelpers.updateChart", chartInstance, newData);}private async Task SwitchToProfitChart(){isSalesData = !isSalesData;var newData = isSalesData ? new { labels = new[] { "Q1", "Q2", "Q3", "Q4" },datasets = new[] {new {label = "销售额",data = new[] { 100, 120, 110, 130 },backgroundColor = "rgba(54, 162, 235, 0.5)"}}} :new {labels = new[] { "Q1", "Q2", "Q3", "Q4" },datasets = new[] {new {label = "利润",data = new[] { 30, 45, 35, 50 },backgroundColor = "rgba(75, 192, 192, 0.5)"}}};await JSRuntime.InvokeVoidAsync("chartHelpers.updateChart", chartInstance, newData);}public async void Dispose(){if (chartInstance != null){await JSRuntime.InvokeVoidAsync("chartHelpers.destroyChart", chartInstance);}}
}

image

image

常见问题与解决方案

问题1:JS互操作调用失败

症状:控制台报错,函数未定义

解决

try 
{await JSRuntime.InvokeVoidAsync("someFunction");
}
catch (JSException ex)
{Console.WriteLine($"JS调用失败: {ex.Message}");// 回退方案await JSRuntime.InvokeVoidAsync("console.warn", "功能不可用");
}

问题2:性能优化

对于频繁调用的JS函数,可以使用IJSInProcessRuntime

@inject IJSRuntime JSRuntime@code {private IJSInProcessRuntime jsInProcess;protected override void OnInitialized(){jsInProcess = (IJSInProcessRuntime)JSRuntime;}private void HandleInput(ChangeEventArgs e){// 同步调用,更高效jsInProcess.InvokeVoidAsync("handleInput", e.Value.ToString());}
}

问题3:组件销毁时资源清理

@implements IDisposable@code {private DotNetObjectReference<MyComponent> dotNetRef;private IJSObjectReference jsModule;protected override async Task OnInitializedAsync(){dotNetRef = DotNetObjectReference.Create(this);jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/myModule.js");}public async void Dispose(){dotNetRef?.Dispose();if (jsModule != null){await jsModule.DisposeAsync();}}
}

Blazor的JavaScript互操作其实没那么难的。记住这几个关键点:

  • IJSRuntime 是你的通行证
  • InvokeVoidAsyncInvokeAsync 是主要工具
  • [JSInvokable] 让.NET方法对JS可见
  • 及时清理资源 很重要

现在你已经掌握了Blazor与JavaScript互操作的核心技能!试着在自己的项目中实践一下,示例源码更放在仓库:https://github.com/shenchuanchao/BlazorApp/tree/master/BlazorAppWasm/Pages

​以上就是《ASP.NET Core Blazor 核心功能二:Blazor与JavaScript互操作——让Web开发更灵活》的全部内容,希望你有所收获。关注、点赞,持续分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/957153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10-15

(1)今天预习了java的课程 (2)明天继续深造

10-14

(1)今天预习了java的课程 (2)明天继续深造

模拟赛 32

没打。USS Enrerprise(CVN-80) 什么时候服役我什么时候补。

top 命令的load average和vmstat 的r列和b列的关系是什么?区别又是什么?

最近在复习linux系统负载问题排查,主要涉及到cpu,内存,io,网络等指标的一些习惯的linux命令,发现top命令的load average指标和vmstat指标的r列以及b列似乎有些联系,但是又搞不懂具体是什么关系,又有什么区别,于是…

高级程序语言设计第4次作业

这个作业属于:https://edu.cnblogs.com/campus/fzu/gjyycx 这个作业的要求:https://edu.cnblogs.com/campus/fzu/gjyycx/homework/14577 学号:102500416 姓名:王浩宇 第一项 运行以下代码12第二项老师布置的题目1d…

11/5

补课恢复了,三个家长找我补课,我现在真是爆炸有钱,然后再备考六级

网页打包EXE/APK/IPA出现乱码时怎么回事?

最近有几个朋友在把网页项目打包成 EXE(电脑程序)、网页打包APK(安卓应用) 或 网页打包IPA(苹果应用) 软件时,会遇到一个恼人的问题: 页面出现乱码, 原本在浏览器中一切正常,文字清晰可读,但一打包成应用,界面上的…

Ai元人文:个人阐述疏漏声明与系统性术语修正说明

Ai元人文:个人阐述疏漏声明与系统性术语修正说明 在“AI元人文”理论体系的构建与演进过程中,我的认知亦在不断深化。我意识到,过往在多篇文章中使用的、以“博弈”为核心的一组关键术语,其表述未能精准传达理论的…

基于AWS构建的微服务集群的最佳实践

在AWS上部署微服务集群时,限流、熔断、降级的最佳实践以AWS托管服务为核心,结合开源组件分层实现——既利用AWS的高可用性和托管优势减少运维成本,又通过标准化开源工具保证灵活性,同时遵循“边缘防护+服务内防护”…

六校联考 20251105C. 物品采购(judge)

为什么写这个题解呢,因为感觉真的很久都没有写过这种屎长代码了。 \(type=1\) 手画不难发现,如果一个序列有 \(\le 2\) 个颜色段,那么它的贡献是 \(0\),因为任意一个子序列都会是子段。 否则贡献是 \(n-2\),因为可…

k3s安装metallb负载均衡

先记录配置过程,后续补充详细介绍1.安装metallb负载均衡器 1.1.配置内核转发参数 sudo tee /etc/sysctl.d/90-k8s-lb.conf <<EOF # 打开路由转发(MetalLB 必需) net.ipv4.ip_forward = 1 # 让 speaker 能及时…

PG故障处理:PG_AUTO_FAILOVER自动切换失败的故障处理

我们的文章会在微信公众号IT民工的龙马人生和博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢! 由于博客中有大量代码,通过页面浏览效果更佳。1、故障背景…

读书笔记:分区不一定能让查询更快——关键要看使用场景

我们的文章会在微信公众号IT民工的龙马人生和博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢! 由于博客中有大量代码,通过页面浏览效果更佳。本文为个人学…

quick save

s and l群星联结,调不动了; // code by 樓影沫瞬_Hz17 #include <iostream> #include <map> #include <vector> #include <algorithm>struct Chara; struct Talent; struct Attack;int turn…

cg0EoeZwd/bdvtAmh0q4PjjA4Pc=

这是郑州西亚斯学院智能体创新大赛的示例文件,如果你看到这个信息,说明这个文件的内容已经正常发送。