嗨,大家好!我是码农刚子。今天我们来聊聊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!");
- }
- }
复制代码
就两行关键代码:
- @inject IJSRuntime JSRuntime - 拿到通行证
- InvokeVoidAsync - 调用不返回值的JS函数
实际场景:比如用户完成某个操作后,你想显示一个提示,用这种方式就特别方便。
2. 调用JavaScript函数 - 不只是简单弹窗
当然,我们不会只满足于弹窗。来看看更实用的例子:
首先,在wwwroot/index.html中添加我们的JavaScript工具函数:- [/code]然后在Blazor组件中使用:
- [code]@inject IJSRuntime JSRuntime
- <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))
- {
-
- <strong>浏览器信息:</strong> @browserInfo
-
- }
- @if (discountResult > 0)
- {
-
- <strong>折扣价格:</strong> ¥@discountResult
-
- }
- @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; }
- }
- }
复制代码
Note
- 使用InvokeVoidAsync调用不返回值的函数
- 使用InvokeAsync调用有返回值的函数,记得指定返回类型
- 复杂对象会自动序列化/反序列化
3. 把.NET方法暴露给JavaScript - 双向操作
有时候,我们也需要让JavaScript能调用我们的C#方法。这就用到[JSInvokable]特性了。- @inject IJSRuntime JSRuntime
- @implements IDisposable
- <h3>.NET方法暴露演示</h3>
-
-
- <label>消息内容:</label>
- <input @bind="message" class="form-control" />
-
-
-
- <label>重复次数:</label>
- <input type="number" @bind="repeatCount" class="form-control" />
-
-
- <button @onclick="RegisterDotNetMethods" class="btn btn-primary">
- 注册.NET方法给JavaScript使用
- </button>
-
-
-
-
- @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);
- }
- }
复制代码
Note
- 记得使用DotNetObjectReference来创建引用
- 使用Dispose()及时清理资源
- 异步方法要返回Task或Task
4. 使用JavaScript库 - 集成第三方神器
这是最实用的部分!让我们看看如何集成流行的JavaScript库。
示例:集成Chart.js图表库
首先引入Chart.js:- [/code]创建图表辅助函数:
- [code]// 在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
- <h3>销售数据图表</h3>
- <canvas id="salesChart" width="400" height="200"></canvas>
-
-
- <button @onclick="LoadSalesData" class="btn btn-primary">加载销售数据</button>
- <button @onclick="SwitchToProfitChart" class="btn btn-secondary">切换到利润图表</button>
-
- @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);
- }
- }
- }
复制代码
常见问题与解决方案
问题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 是你的通行证
- InvokeVoidAsync 和 InvokeAsync 是主要工具
- [JSInvokable] 让.NET方法对JS可见
- 及时清理资源 很重要
现在你已经掌握了Blazor与JavaScript互操作的核心技能!试着在自己的项目中实践一下,示例源码更放在仓库:https://github.com/shenchuanchao/BlazorApp/tree/master/BlazorAppWasm/Pages
以上就是《ASP.NET Core Blazor 核心功能二:Blazor与JavaScript互操作——让Web开发更灵活》的全部内容,希望你有所收获。关注、点赞,持续分享。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |