Blazor Server中基于JSInterop实现Chart.js实时图表更新问询
Blazor Server集成Chart.js实现实时更新图表方案
问题背景
我在Blazor Server应用中通过JSInterop集成了Chart.js库,当前折线图可正常展示20个测量值,但希望将其改造为实时图表,实现每秒新增数据时自动更新图表,以下是现有代码:
现有页面代码 (/wmi_performance2)
@page "/wmi_performance2" @inject TagService TagService @using System.IO @inject IJSRuntime JSRuntime <Chart Id="bar1" Type="@Chart.ChartType.Bar" Data="@CPU_Load_array" Labels="@Measuring_points_array"> </Chart>
现有Chart组件代码 (Shared/Components/Chart.razor)
@using System.IO @inject IJSRuntime JSRuntime @using Microsoft.JSInterop; @inject IJSRuntime jsInvoker <canvas id="@Id"></canvas> @code { public enum ChartType { Pie, Bar } [Parameter] public string Id { get; set; } [Parameter] public ChartType Type { get; set; } [Parameter] public string[] Data { get; set; } [Parameter] public string[] BackgroundColor { get; set; } [Parameter] public string[] Labels { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { try{ var config = new { Type = Type.ToString().ToLower(), Options = new { Responsive = true, Scales = new { YAxes = new[] { new { Ticks = new { BeginAtZero=true } } } } }, Data = new { Datasets = new[] { new { Data = Data, BackgroundColor = BackgroundColor} }, Labels = Labels } }; await JSRuntime.InvokeVoidAsync("setup", Id, config); } catch(Exception e) { using (StreamWriter sw = File.AppendText((Pages.CommonClass.error_path))) { sw.WriteLine("Chart.JS Error: " + e + Environment.NewLine); } } } }
现有JS代码 (wwwroot/chart.js)
window.setup = (id, config) => { var ctx = document.getElementById(id).getContext('2d'); new Chart(ctx, config); }
实现方案
1. 改造JS代码,存储Chart实例并添加更新方法
原来的setup函数每次都会创建新图表,我们需要把实例存在全局对象中,方便后续调用更新方法:
// 全局存储Chart实例,避免重复创建 window.chartInstances = {}; window.setup = (id, config) => { const ctx = document.getElementById(id).getContext('2d'); // 销毁已存在的实例,防止重复渲染 if (window.chartInstances[id]) { window.chartInstances[id].destroy(); } window.chartInstances[id] = new Chart(ctx, config); }; // 新增更新图表数据的方法 window.updateChartData = (id, newData, newLabel) => { const chart = window.chartInstances[id]; if (!chart) return; // 添加新数据和标签 chart.data.labels.push(newLabel); chart.data.datasets[0].data.push(newData); // 保持最多20个数据点(可按需调整) if (chart.data.labels.length > 20) { chart.data.labels.shift(); chart.data.datasets[0].data.shift(); } // 通知Chart.js更新视图 chart.update(); };
2. 改造Chart组件,添加更新方法
给Chart组件添加一个公开的UpdateChart方法,用于触发JS更新,同时兼容折线图类型:
@using System.IO @inject IJSRuntime JSRuntime <canvas id="@Id"></canvas> @code { public enum ChartType { Pie, Bar, Line // 补充折线图类型 } [Parameter] public string Id { get; set; } [Parameter] public ChartType Type { get; set; } [Parameter] public string[] Data { get; set; } [Parameter] public string[] BackgroundColor { get; set; } [Parameter] public string[] Labels { get; set; } // 公开给父组件调用的更新方法 public async Task UpdateChart(string newDataPoint, string newLabel) { await JSRuntime.InvokeVoidAsync("updateChartData", Id, newDataPoint, newLabel); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { try { // 适配Chart.js v3+ 坐标轴配置(如果用v2版本,改回YAxes即可) var config = new { Type = Type.ToString().ToLower(), Options = new { Responsive = true, Scales = new { Y = new { Ticks = new { BeginAtZero = true } }, X = new { Ticks = new { AutoSkip = true, MaxRotation = 0 } } } }, Data = new { Datasets = new[] { new { Data = Data, BackgroundColor = BackgroundColor } }, Labels = Labels } }; await JSRuntime.InvokeVoidAsync("setup", Id, config); } catch (Exception e) { using (StreamWriter sw = File.AppendText(Pages.CommonClass.error_path)) { sw.WriteLine("Chart.JS Error: " + e + Environment.NewLine); } } } } }
3. 页面中实现定时获取数据并更新图表
在页面中添加定时器,每秒获取新数据并调用Chart组件的更新方法,同时注意清理定时器避免内存泄漏:
@page "/wmi_performance2" @inject TagService TagService @using System.IO @inject IJSRuntime JSRuntime @implements IDisposable <Chart @ref="chartRef" Id="bar1" Type="@Chart.ChartType.Line" Data="@CPU_Load_array" Labels="@Measuring_points_array" BackgroundColor="@backgroundColors"> </Chart> @code { private Chart chartRef; private string[] CPU_Load_array = new string[20]; // 初始20个数据 private string[] Measuring_points_array = new string[20]; // 初始标签 private string[] backgroundColors = Enumerable.Repeat("rgba(75, 192, 192, 0.2)", 20).ToArray(); private Timer timer; protected override void OnInitialized() { // 初始化初始数据(此处替换为你的实际数据获取逻辑) // CPU_Load_array = await TagService.GetInitialCpuData(); // Measuring_points_array = ... // 启动定时器,每秒触发一次数据更新 timer = new Timer(async _ => await UpdateChartData(), null, TimeSpan.Zero, TimeSpan.FromSeconds(1)); } private async Task UpdateChartData() { // 从服务获取最新测量值(替换为你的实际数据接口) var newCpuLoad = await TagService.GetLatestCpuLoad(); var newLabel = DateTime.Now.ToString("HH:mm:ss"); // 用当前时间作为标签 // 同步更新组件内的数组(可选,保持和图表数据一致) Array.Resize(ref CPU_Load_array, CPU_Load_array.Length + 1); CPU_Load_array[CPU_Load_array.Length - 1] = newCpuLoad.ToString(); Array.Resize(ref Measuring_points_array, Measuring_points_array.Length + 1); Measuring_points_array[Measuring_points_array.Length - 1] = newLabel; // 保持数组长度不超过20 if (CPU_Load_array.Length > 20) { CPU_Load_array = CPU_Load_array.Skip(1).ToArray(); Measuring_points_array = Measuring_points_array.Skip(1).ToArray(); } // 调用Chart组件的更新方法 if (chartRef != null) { await chartRef.UpdateChart(newCpuLoad.ToString(), newLabel); } } // 销毁定时器,防止内存泄漏 public void Dispose() { timer?.Dispose(); } }
关键注意事项
- Chart实例管理:必须将Chart实例存储在全局对象中,否则每次更新都会创建新图表,导致性能问题和视图混乱。
- Timer清理:Blazor Server中使用
Timer必须实现IDisposable接口,在组件销毁时清理定时器,避免内存泄漏。 - 数据裁剪:通过
shift()方法移除最旧的数据,保持图表只显示最近20个点,避免数据过多导致图表拥挤。 - 版本兼容:如果使用Chart.js v2版本,需要将坐标轴配置改回
YAxes和XAxes的写法。
内容的提问来源于stack exchange,提问作者Mdarende
相关产品推荐
相关产品推荐

