You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 18:35:28