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

嗨,大家好!我是码农刚子。今天我们来聊聊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!");     } }

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

就两行关键代码:

  • @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; }     } }

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

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

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);     } }

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

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);         }     } }

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

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

常见问题与解决方案

问题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开发更灵活》的全部内容,希望你有所收获。关注、点赞,持续分享

发表评论

评论已关闭。

相关文章