找回密码
 立即注册
首页 业界区 业界 ASP.NET Core Blazor 核心功能三:Blazor与JavaScript互 ...

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

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

想象一下:你正在用Blazor开发一个超棒的应用,但突然需要用到某个只有JavaScript才能实现的炫酷效果,或者要集成一个超好用的第三方JS库。这时候,JavaScript互操作就是你的救星!
简单来说,它让Blazor和JavaScript可以"握手合作",各展所长。下面我们就从最基础的部分开始。
1. IJSRuntime - 你的JavaScript通行证

在Blazor中,IJSRuntime是与JavaScript沟通的桥梁。获取它超级简单:
  1. @inject IJSRuntime JSRuntime
  2. <button @onclick="ShowAlert">点我弹窗!</button>
  3. @code {
  4.     private async Task ShowAlert()
  5.     {
  6.         await JSRuntime.InvokeVoidAsync("alert", "Hello from Blazor!");
  7.     }
  8. }
复制代码
1.png

就两行关键代码:

  • @inject IJSRuntime JSRuntime - 拿到通行证
  • InvokeVoidAsync - 调用不返回值的JS函数
实际场景:比如用户完成某个操作后,你想显示一个提示,用这种方式就特别方便。
2. 调用JavaScript函数 - 不只是简单弹窗

当然,我们不会只满足于弹窗。来看看更实用的例子:
首先,在wwwroot/index.html中添加我们的JavaScript工具函数:
  1. [/code]然后在Blazor组件中使用:
  2. [code]@inject IJSRuntime JSRuntime
  3.     <h3>JavaScript函数调用演示</h3>
  4.    
  5.     <button @onclick="ShowSuccessNotification" class="btn btn-success">
  6.         显示成功提示
  7.     </button>
  8.    
  9.     <button @onclick="ShowErrorNotification" class="btn btn-danger">
  10.         显示错误提示
  11.     </button>
  12.    
  13.     <button @onclick="GetBrowserInfo" class="btn btn-info">
  14.         获取浏览器信息
  15.     </button>
  16.    
  17.     <button @onclick="CalculatePrice" class="btn btn-warning">
  18.         计算折扣价格
  19.     </button>
  20.     @if (!string.IsNullOrEmpty(browserInfo))
  21.     {
  22.         
  23.             <strong>浏览器信息:</strong> @browserInfo
  24.         
  25.     }
  26.     @if (discountResult > 0)
  27.     {
  28.         
  29.             <strong>折扣价格:</strong> ¥@discountResult
  30.         
  31.     }
  32. @code {
  33.     private string browserInfo = "";
  34.     private decimal discountResult;
  35.     private async Task ShowSuccessNotification()
  36.     {
  37.         await JSRuntime.InvokeVoidAsync("myJsHelpers.showNotification",
  38.             "操作成功!数据已保存。", "success");
  39.     }
  40.     private async Task ShowErrorNotification()
  41.     {
  42.         await JSRuntime.InvokeVoidAsync("myJsHelpers.showNotification",
  43.             "出错了!请检查网络连接。", "error");
  44.     }
  45.     private async Task GetBrowserInfo()
  46.     {
  47.         var info = await JSRuntime.InvokeAsync<BrowserInfo>("myJsHelpers.getBrowserInfo");
  48.         browserInfo = $"语言: {info.Language}, 平台: {info.Platform}";
  49.     }
  50.     private async Task CalculatePrice()
  51.     {
  52.         discountResult = await JSRuntime.InvokeAsync<decimal>(
  53.             "myJsHelpers.calculateDiscount", 1000, 20); // 原价1000,8折
  54.     }
  55.     // 定义接收复杂对象的类
  56.     private class BrowserInfo
  57.     {
  58.         public string UserAgent { get; set; }
  59.         public string Language { get; set; }
  60.         public string Platform { get; set; }
  61.     }
  62. }
复制代码
2.png

3.png

Note


  • 使用InvokeVoidAsync调用不返回值的函数
  • 使用InvokeAsync调用有返回值的函数,记得指定返回类型
  • 复杂对象会自动序列化/反序列化
3. 把.NET方法暴露给JavaScript - 双向操作

有时候,我们也需要让JavaScript能调用我们的C#方法。这就用到[JSInvokable]特性了。
  1. @inject IJSRuntime JSRuntime
  2. @implements IDisposable
  3.     <h3>.NET方法暴露演示</h3>
  4.    
  5.    
  6.         <label>消息内容:</label>
  7.         <input @bind="message" class="form-control" />
  8.    
  9.    
  10.    
  11.         <label>重复次数:</label>
  12.         <input type="number" @bind="repeatCount" class="form-control" />
  13.    
  14.    
  15.     <button @onclick="RegisterDotNetMethods" class="btn btn-primary">
  16.         注册.NET方法给JavaScript使用
  17.     </button>
  18.    
  19.    
  20.         
  21.    
  22. @code {
  23.     private string message = "Hello from .NET!";
  24.     private int repeatCount = 3;
  25.     private DotNetObjectReference<MyComponent> dotNetHelper;
  26.     protected override void OnInitialized()
  27.     {
  28.         dotNetHelper = DotNetObjectReference.Create(this);
  29.     }
  30.     private async Task RegisterDotNetMethods()
  31.     {
  32.         await JSRuntime.InvokeVoidAsync("registerDotNetHelper", dotNetHelper);
  33.     }
  34.     [JSInvokable]
  35.     public string GetFormattedMessage()
  36.     {
  37.         return string.Join(" ", Enumerable.Repeat(message, repeatCount));
  38.     }
  39.     [JSInvokable]
  40.     public async Task<string> ProcessDataAsync(string input)
  41.     {
  42.         // 模拟一些异步处理
  43.         await Task.Delay(500);
  44.         return $"处理后的数据: {input.ToUpper()} (处理时间: {DateTime.Now:HH:mm:ss})";
  45.     }
  46.     [JSInvokable]
  47.     public void ShowAlert(string alertMessage)
  48.     {
  49.         // 这个方法会被JavaScript调用
  50.         // 在实际应用中,你可能会更新组件状态或触发其他操作
  51.         Console.WriteLine($"收到JavaScript的警告: {alertMessage}");
  52.     }
  53.     public void Dispose()
  54.     {
  55.         dotNetHelper?.Dispose();
  56.     }
  57. }
复制代码
对应的JavaScript代码:
  1. // 在index.html中添加
  2. function registerDotNetHelper(dotNetHelper) {
  3.     // 存储.NET引用供后续使用
  4.     window.dotNetHelper = dotNetHelper;
  5.    
  6.     // 演示调用.NET方法
  7.     callDotNetMethods();
  8. }
  9. async function callDotNetMethods() {
  10.     if (!window.dotNetHelper) {
  11.         console.error('.NET helper 未注册');
  12.         return;
  13.     }
  14.    
  15.     try {
  16.         // 调用无参数的.NET方法
  17.         const message = await window.dotNetHelper.invokeMethodAsync('GetFormattedMessage');
  18.         
  19.         // 调用带参数的异步.NET方法
  20.         const processed = await window.dotNetHelper.invokeMethodAsync('ProcessDataAsync', 'hello world');
  21.         
  22.         // 调用void方法
  23.         window.dotNetHelper.invokeMethodAsync('ShowAlert', '这是从JS发来的消息!');
  24.         
  25.         // 在页面上显示结果
  26.         const output = document.getElementById('js-output');
  27.         output.innerHTML = `
  28.             <strong>来自.NET的消息:</strong> ${message}<br>
  29.             <strong>处理后的数据:</strong> ${processed}
  30.         `;
  31.         
  32.     } catch (error) {
  33.         console.error('调用.NET方法失败:', error);
  34.     }
  35. }
复制代码
4.png

Note


  • 记得使用DotNetObjectReference来创建引用
  • 使用Dispose()及时清理资源
  • 异步方法要返回Task或Task
4. 使用JavaScript库 - 集成第三方神器

这是最实用的部分!让我们看看如何集成流行的JavaScript库。
示例:集成Chart.js图表库

首先引入Chart.js:
  1. [/code]创建图表辅助函数:
  2. [code]// 在index.html中或单独的JS文件
  3. window.chartHelpers = {
  4.     createChart: function (canvasId, config) {
  5.         const ctx = document.getElementById(canvasId).getContext('2d');
  6.         return new Chart(ctx, config);
  7.     },
  8.    
  9.     updateChart: function (chart, data) {
  10.         chart.data = data;
  11.         chart.update();
  12.     },
  13.    
  14.     destroyChart: function (chart) {
  15.         chart.destroy();
  16.     }
  17. };
复制代码
Blazor组件:
  1. @inject IJSRuntime JSRuntime
  2. @implements IDisposable
  3.     <h3>销售数据图表</h3>
  4.     <canvas id="salesChart" width="400" height="200"></canvas>
  5.    
  6.    
  7.         <button @onclick="LoadSalesData" class="btn btn-primary">加载销售数据</button>
  8.         <button @onclick="SwitchToProfitChart" class="btn btn-secondary">切换到利润图表</button>
  9.    
  10. @code {
  11.     private IJSObjectReference chartInstance;
  12.     private bool isSalesData = true;
  13.     protected override async Task OnAfterRenderAsync(bool firstRender)
  14.     {
  15.         if (firstRender)
  16.         {
  17.             await InitializeChart();
  18.         }
  19.     }
  20.     private async Task InitializeChart()
  21.     {
  22.         var config = new
  23.         {
  24.             type = "bar",
  25.             data = new
  26.             {
  27.                 labels = new[] { "一月", "二月", "三月", "四月", "五月" },
  28.                 datasets = new[]
  29.                 {
  30.                     new
  31.                     {
  32.                         label = "销售额",
  33.                         data = new[] { 65, 59, 80, 81, 56 },
  34.                         backgroundColor = "rgba(54, 162, 235, 0.5)",
  35.                         borderColor = "rgba(54, 162, 235, 1)",
  36.                         borderWidth = 1
  37.                     }
  38.                 }
  39.             },
  40.             options = new
  41.             {
  42.                 responsive = true,
  43.                 plugins = new
  44.                 {
  45.                     title = new
  46.                     {
  47.                         display = true,
  48.                         text = "月度销售数据"
  49.                     }
  50.                 }
  51.             }
  52.         };
  53.         chartInstance = await JSRuntime.InvokeAsync<IJSObjectReference>(
  54.             "chartHelpers.createChart", "salesChart", config);
  55.     }
  56.     private async Task LoadSalesData()
  57.     {
  58.         var newData = new
  59.         {
  60.             labels = new[] { "一月", "二月", "三月", "四月", "五月", "六月" },
  61.             datasets = new[]
  62.             {
  63.                 new
  64.                 {
  65.                     label = "销售额",
  66.                     data = new[] { 65, 59, 80, 81, 56, 75 },
  67.                     backgroundColor = "rgba(54, 162, 235, 0.5)"
  68.                 }
  69.             }
  70.         };
  71.         await JSRuntime.InvokeVoidAsync("chartHelpers.updateChart",
  72.             chartInstance, newData);
  73.     }
  74.     private async Task SwitchToProfitChart()
  75.     {
  76.         isSalesData = !isSalesData;
  77.         
  78.         var newData = isSalesData ?
  79.             new {
  80.                 labels = new[] { "Q1", "Q2", "Q3", "Q4" },
  81.                 datasets = new[] {
  82.                     new {
  83.                         label = "销售额",
  84.                         data = new[] { 100, 120, 110, 130 },
  85.                         backgroundColor = "rgba(54, 162, 235, 0.5)"
  86.                     }
  87.                 }
  88.             } :
  89.             new {
  90.                 labels = new[] { "Q1", "Q2", "Q3", "Q4" },
  91.                 datasets = new[] {
  92.                     new {
  93.                         label = "利润",
  94.                         data = new[] { 30, 45, 35, 50 },
  95.                         backgroundColor = "rgba(75, 192, 192, 0.5)"
  96.                     }
  97.                 }
  98.             };
  99.         await JSRuntime.InvokeVoidAsync("chartHelpers.updateChart",
  100.             chartInstance, newData);
  101.     }
  102.     public async void Dispose()
  103.     {
  104.         if (chartInstance != null)
  105.         {
  106.             await JSRuntime.InvokeVoidAsync("chartHelpers.destroyChart", chartInstance);
  107.         }
  108.     }
  109. }
复制代码
5.png

6.png

常见问题与解决方案

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

症状:控制台报错,函数未定义
解决
  1. try
  2. {
  3.     await JSRuntime.InvokeVoidAsync("someFunction");
  4. }
  5. catch (JSException ex)
  6. {
  7.     Console.WriteLine($"JS调用失败: {ex.Message}");
  8.     // 回退方案
  9.     await JSRuntime.InvokeVoidAsync("console.warn", "功能不可用");
  10. }
复制代码
问题2:性能优化

对于频繁调用的JS函数,可以使用IJSInProcessRuntime:
  1. @inject IJSRuntime JSRuntime
  2. @code {
  3.     private IJSInProcessRuntime jsInProcess;
  4.     protected override void OnInitialized()
  5.     {
  6.         jsInProcess = (IJSInProcessRuntime)JSRuntime;
  7.     }
  8.     private void HandleInput(ChangeEventArgs e)
  9.     {
  10.         // 同步调用,更高效
  11.         jsInProcess.InvokeVoidAsync("handleInput", e.Value.ToString());
  12.     }
  13. }
复制代码
问题3:组件销毁时资源清理
  1. @implements IDisposable
  2. @code {
  3.     private DotNetObjectReference<MyComponent> dotNetRef;
  4.     private IJSObjectReference jsModule;
  5.     protected override async Task OnInitializedAsync()
  6.     {
  7.         dotNetRef = DotNetObjectReference.Create(this);
  8.         jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>(
  9.             "import", "./js/myModule.js");
  10.     }
  11.     public async void Dispose()
  12.     {
  13.         dotNetRef?.Dispose();
  14.         
  15.         if (jsModule != null)
  16.         {
  17.             await jsModule.DisposeAsync();
  18.         }
  19.     }
  20. }
复制代码
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开发更灵活》的全部内容,希望你有所收获。关注、点赞,持续分享
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册